Merge branch 'mh/mmap-packed-refs'
authorJunio C Hamano <gitster@pobox.com>
Tue, 3 Oct 2017 06:42:50 +0000 (15:42 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 3 Oct 2017 06:42:50 +0000 (15:42 +0900)
Operations that do not touch (majority of) packed refs have been
optimized by making accesses to packed-refs file lazy; we no longer
pre-parse everything, and an access to a single ref in the
packed-refs does not touch majority of irrelevant refs, either.

* mh/mmap-packed-refs: (21 commits)
packed-backend.c: rename a bunch of things and update comments
mmapped_ref_iterator: inline into `packed_ref_iterator`
ref_cache: remove support for storing peeled values
packed_ref_store: get rid of the `ref_cache` entirely
ref_store: implement `refs_peel_ref()` generically
packed_read_raw_ref(): read the reference from the mmapped buffer
packed_ref_iterator_begin(): iterate using `mmapped_ref_iterator`
read_packed_refs(): ensure that references are ordered when read
packed_ref_cache: keep the `packed-refs` file mmapped if possible
packed-backend.c: reorder some definitions
mmapped_ref_iterator_advance(): no peeled value for broken refs
mmapped_ref_iterator: add iterator over a packed-refs file
packed_ref_cache: remember the file-wide peeling state
read_packed_refs(): read references with minimal copying
read_packed_refs(): make parsing of the header line more robust
read_packed_refs(): only check for a header at the top of the file
read_packed_refs(): use mmap to read the `packed-refs` file
die_unterminated_line(), die_invalid_line(): new functions
packed_ref_cache: add a backlink to the associated `packed_ref_store`
prefix_ref_iterator: break when we leave the prefix
...

267 files changed:
.clang-format [new file with mode: 0644]
.travis.yml
.tsan-suppressions [new file with mode: 0644]
Documentation/RelNotes/2.10.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.11.4.txt [new file with mode: 0644]
Documentation/RelNotes/2.12.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.13.6.txt [new file with mode: 0644]
Documentation/RelNotes/2.14.2.txt
Documentation/RelNotes/2.15.0.txt
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-apply.txt
Documentation/git-branch.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-filter-branch.txt
Documentation/git-for-each-ref.txt
Documentation/git-grep.txt
Documentation/git-merge.txt
Documentation/git-notes.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-rerere.txt
Documentation/git-rev-parse.txt
Documentation/git-rm.txt
Documentation/git-shell.txt
Documentation/git-status.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcore-tutorial.txt
Documentation/githooks.txt
Documentation/gitrepository-layout.txt
Documentation/gittutorial.txt
Documentation/glossary-content.txt
Documentation/merge-options.txt
Documentation/merge-strategies.txt
Documentation/rev-list-options.txt
Documentation/technical/api-config.txt
Documentation/technical/api-ref-iteration.txt
Documentation/technical/api-string-list.txt [deleted file]
Documentation/technical/pack-protocol.txt
Documentation/technical/trivial-merge.txt
Documentation/user-manual.txt
Makefile
abspath.c
apply.c
archive.c
attr.c
bisect.c
branch.c
builtin/add.c
builtin/am.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/check-ref-format.c
builtin/checkout.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/fsck.c
builtin/gc.c
builtin/get-tar-commit-id.c
builtin/help.c
builtin/init-db.c
builtin/log.c
builtin/ls-files.c
builtin/merge.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/pull.c
builtin/rebase--helper.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote-ext.c
builtin/remote.c
builtin/rerere.c
builtin/reset.c
builtin/rev-parse.c
builtin/shortlog.c
builtin/submodule--helper.c
builtin/symbolic-ref.c
builtin/unpack-file.c
builtin/update-index.c
builtin/worktree.c
bulk-checkin.c
bundle.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-slab.h
commit.c
commit.h
compat/win32/lazyload.h [new file with mode: 0644]
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
csum-file.c
diff-delta.c
diff-lib.c
diff.c
dir.c
entry.c
environment.c
exec_cmd.c
fast-import.c
fsck.c
git-archimport.perl
git-compat-util.h
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-gui/po/README
git-merge-octopus.sh
git-p4.py
git-rebase--interactive.sh
git-rebase.sh
git-send-email.perl
git.c
gpg-interface.c
hash.h
hashmap.c
hashmap.h
http-backend.c
http-push.c
http.c
imap-send.c
line-log.c
line-log.h
list.h
ll-merge.c
lockfile.c
lockfile.h
log-tree.c
mailinfo.c
merge-recursive.c
merge-recursive.h
name-hash.c
notes-merge.c
notes.c
object.c
object.h
outgoing/packfile.h [deleted file]
pack-bitmap-write.c
pack-bitmap.c
pack-write.c
packfile.c
parse-options.c
path.c
pathspec.c
pathspec.h
pkt-line.c
quote.c
reachable.c
read-cache.c
refs.c
refs.h
refs/files-backend.c
refs/packed-backend.c
refs/refs-internal.h
remote.c
repository.c
rerere.c
revision.c
revision.h
send-pack.c
sequencer.c
sequencer.h
setup.c
sha1-lookup.h
sha1_file.c
sha1dc_git.c
sha1dc_git.h
shallow.c
shell.c
strbuf.h
streaming.c
string-list.h
sub-process.c
submodule.c
submodule.h
t/README
t/check-non-portable-shell.pl
t/helper/.gitignore
t/helper/test-delta.c
t/helper/test-hashmap.c
t/helper/test-line-buffer.c
t/helper/test-parse-options.c
t/t0040-parse-options.sh
t/t1400-update-ref.sh
t/t1407-worktree-ref-store.sh
t/t1500-rev-parse.sh
t/t1502-rev-parse-parseopt.sh
t/t3200-branch.sh
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t4013-diff-various.sh
t/t4013/diff.diff-tree_--stat_initial_mode [new file with mode: 0644]
t/t4013/diff.diff-tree_--summary_initial_mode [new file with mode: 0644]
t/t4013/diff.diff-tree_initial_mode [new file with mode: 0644]
t/t4013/diff.log_--decorate=full_--all
t/t4013/diff.log_--decorate_--all
t/t4059-diff-submodule-not-initialized.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/t6132-pathspec-exclude.sh
t/t7001-mv.sh
t/t7004-tag.sh
t/t7406-submodule-update.sh
t/t7504-commit-msg-hook.sh
t/t7508-status.sh
t/t8010-cat-file-filters.sh
t/t9001-send-email.sh
t/t9010-svn-fe.sh
t/t9350-fast-export.sh
t/t9400-git-cvsserver-server.sh
t/test-lib.sh
tempfile.c
tempfile.h
templates/hooks--pre-rebase.sample
trailer.c
transport-helper.c
transport.c
tree-walk.c
unpack-trees.c
upload-pack.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$
diff --git a/Documentation/RelNotes/2.10.5.txt b/Documentation/RelNotes/2.10.5.txt
new file mode 100644 (file)
index 0000000..a498fd6
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.10.5 Release Notes
+=========================
+
+Fixes since v2.10.4
+-------------------
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
+
diff --git a/Documentation/RelNotes/2.11.4.txt b/Documentation/RelNotes/2.11.4.txt
new file mode 100644 (file)
index 0000000..ad4da8e
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.11.4 Release Notes
+=========================
+
+Fixes since v2.11.3
+-------------------
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
+
diff --git a/Documentation/RelNotes/2.12.5.txt b/Documentation/RelNotes/2.12.5.txt
new file mode 100644 (file)
index 0000000..8fa73cf
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.12.5 Release Notes
+=========================
+
+Fixes since v2.12.4
+-------------------
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
+
diff --git a/Documentation/RelNotes/2.13.6.txt b/Documentation/RelNotes/2.13.6.txt
new file mode 100644 (file)
index 0000000..afcae9c
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.13.6 Release Notes
+=========================
+
+Fixes since v2.13.5
+-------------------
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
+
index 5517afcf5920b8fa023e2ba0babf6c4a1d92509b..bec9186adefe6eeec3d2dd18c871f5fd2047ff6a 100644 (file)
@@ -32,4 +32,74 @@ 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.
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
 Also contains various documentation updates and code clean-ups.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
index cb630724c7c9172c56630d1cc27525e187d9da39..8a869e4ef1d6a620bbb76e85540f9e36e6b95fb2 100644 (file)
@@ -18,6 +18,9 @@ Backward compatibility notes and other notable changes.
    might still be cases that need to be addressed--bug reports are
    greatly appreciated.
 
+ * "branch --set-upstream" that has been deprecated in Git 1.8 has
+   finally been retired.
+
 
 Updates since v2.14
 -------------------
@@ -71,6 +74,19 @@ UI, Workflows & Features
    which can make it succeed if the lock holder was holding it during
    a read-only operation.
 
+ * "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.
+
+ * "git rev-parse" learned "--is-shallow-repository", that is to be
+   used in a way similar to existing "--is-bare-repository" and
+   friends.
+
+ * "git describe --match <pattern>" has been taught to play well with
+   the "--all" option.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -127,6 +143,66 @@ 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.
+
+ * "git imap-send" has our own implementation of the protocol and also
+   can use more recent libCurl with the imap protocol support.  Update
+   the latter so that it can use the credential subsystem, and then
+   make it the default option to use, so that we can eventually
+   deprecate and remove the former.
+
+ * "make style" runs git-clang-format to help developers by pointing
+   out coding style issues.
+
+ * A test to demonstrate "git mv" failing to adjust nested submodules
+   has been added.
+   (merge c514167df2 hv/mv-nested-submodules-test later to maint).
+
+ * On Cygwin, "ulimit -s" does not report failure but it does not work
+   at all, which causes an unexpected success of some tests that
+   expect failures under a limited stack situation.  This has been
+   fixed.
+
+ * Many codepaths have been updated to squelch -Wimplicit-fallthrough
+   warnings from Gcc 7 (which is a good code hygiene).
+
+ * Add a helper for DLL loading in anticipation for its need in a
+   future topic RSN.
+
+ * "git status --ignored", when noticing that a directory without any
+   tracked path is ignored, still enumerated all the ignored paths in
+   the directory, which is unnecessary.  The codepath has been
+   optimized to avoid this overhead.
+
 Also contains various documentation updates and code clean-ups.
 
 
@@ -155,7 +231,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
@@ -164,49 +239,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
@@ -215,16 +277,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
@@ -233,34 +292,152 @@ 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.
+
+ * 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).
+
+ * Many codepaths did not diagnose write failures correctly when disks
+   go full, due to their misuse of write_in_full() helper function,
+   which have been corrected.
+   (merge f48ecd38cb jk/write-in-full-fix later to maint).
+
+ * "git help co" now says "co is aliased to ...", not "git co is".
+   (merge b3a8076e0d ks/help-alias-label later to maint).
+
+ * "git archive", especially when used with pathspec, stored an empty
+   directory in its output, even though Git itself never does so.
+   This has been fixed.
+   (merge 4318094047 rs/archive-excluded-directory later to maint).
+
+ * API error-proofing which happens to also squelch warnings from GCC.
+   (merge c788c54cde tg/refs-allowed-flags later to maint).
+
+ * The explanation of the cut-line in the commit log editor has been
+   slightly tweaked.
+   (merge 8c4b1a3593 ks/commit-do-not-touch-cut-line later to maint).
+
+ * "git gc" tries to avoid running two instances at the same time by
+   reading and writing pid/host from and to a lock file; it used to
+   use an incorrect fscanf() format when reading, which has been
+   corrected.
+   (merge afe2fab72c aw/gc-lockfile-fscanf-fix later to maint).
+
+ * The scripts to drive TravisCI has been reorganized and then an
+   optimization to avoid spending cycles on a branch whose tip is
+   tagged has been implemented.
+   (merge 8376eb4a8f ls/travis-scriptify later to maint).
+
+ * The test linter has been taught that we do not like "echo -e".
+   (merge 1a6d46895d tb/test-lint-echo-e later to maint).
+
+ * Code cmp.std.c nitpick.
+   (merge ac7da78ede mh/for-each-string-list-item-empty-fix later to maint).
+
+ * A regression fix for 2.11 that made the code to read the list of
+   alternate object stores overrun the end of the string.
+   (merge f0f7bebef7 jk/info-alternates-fix later to maint).
+
+ * "git describe --match" learned to take multiple patterns in v2.13
+   series, but the feature ignored the patterns after the first one
+   and did not work at all.  This has been fixed.
+   (merge da769d2986 jk/describe-omit-some-refs later to maint).
+
+ * "git filter-branch" cannot reproduce a history with a tag without
+   the tagger field, which only ancient versions of Git allowed to be
+   created.  This has been corrected.
+   (merge b2c1ca6b4b ic/fix-filter-branch-to-handle-tag-without-tagger later to maint).
+
+ * "git cat-file --textconv" started segfaulting recently, which
+   has been corrected.
+   (merge cc0ea7c9e5 jk/diff-blob later to maint).
+
+ * The built-in pattern to detect the "function header" for HTML did
+   not match <H1>..<H6> elements without any attributes, which has
+   been fixed.
+   (merge 9c03caca2c ik/userdiff-html-h-element-fix later to maint).
+
+ * "git mailinfo" was loose in decoding quoted printable and produced
+   garbage when the two letters after the equal sign are not
+   hexadecimal.  This has been fixed.
+   (merge c8cf423eab rs/mailinfo-qp-decode-fix later to maint).
+
+ * The machinery to create xdelta used in pack files received the
+   sizes of the data in size_t, but lost the higher bits of them by
+   storing them in "unsigned int" during the computation, which is
+   fixed.
+
+ * The delta format used in the packfile cannot reference data at
+   offset larger than what can be expressed in 4-byte, but the
+   generator for the data failed to make sure the offset does not
+   overflow.  This has been corrected.
+
+ * The documentation for '-X<option>' for merges was misleadingly
+   written to suggest that "-s theirs" exists, which is not the case.
+   (merge c25d98b2a7 jc/merge-x-theirs-docfix later to maint).
+
+ * "git fast-export" with -M/-C option issued "copy" instruction on a
+   path that is simultaneously modified, which was incorrect.
+   (merge b3e8ca89cf jt/fast-export-copy-modify-fix later to maint).
+
+ * Many codepaths have been updated to squelch -Wsign-compare
+   warnings.
+   (merge 071bcaab64 rj/no-sign-compare later to maint).
+
+ * Memory leaks in various codepaths have been plugged.
+   (merge 4d01a7fa65 ma/leakplugs 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).
+   (merge ab46e6fc72 cc/subprocess-handshake-missing-capabilities later to maint).
+   (merge f7a32dd97f kd/doc-for-each-ref later to maint).
+   (merge be94568bc7 ez/doc-duplicated-words-fix later to maint).
+   (merge 01e4be6c3d ks/test-readme-phrasofix later to maint).
+   (merge 217bb56d4f hn/typofix later to maint).
+   (merge c08fd6388c jk/doc-read-tree-table-asciidoctor-fix later to maint).
+   (merge c3342b362e ks/doc-use-camelcase-for-config-name later to maint).
index dc4e3f58a2d2e8a5a6753bd290d65fec4f7f7b21..1ac0ae6adb0460a3460084aef2302434e24ee6de 100644 (file)
@@ -3085,10 +3085,14 @@ submodule.<name>.url::
        See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
 
 submodule.<name>.update::
-       The default update procedure for a submodule. This variable
-       is populated by `git submodule init` from the
-       linkgit:gitmodules[5] file. See description of 'update'
-       command in linkgit:git-submodule[1].
+       The method by which a submodule is updated by 'git submodule update',
+       which is the only affected command, others such as
+       'git checkout --recurse-submodules' are unaffected. It exists for
+       historical reasons, when 'git submodule' was the only command to
+       interact with submodules; settings like `submodule.active`
+       and `pull.rebase` are more specific. It is populated by
+       `git submodule init` from the linkgit:gitmodules[5] file.
+       See description of 'update' command in linkgit:git-submodule[1].
 
 submodule.<name>.branch::
        The remote branch name for a submodule, used by `git submodule
index f4169fb1ec4c61068cc70756c31a5abcc31496f6..b700beaff5ad2eaa407bfb3818e016a5bb27d87c 100644 (file)
@@ -61,6 +61,9 @@ OPTIONS
        the working tree.  Note that older versions of Git used
        to ignore removed files; use `--no-all` option if you want
        to add modified or new files but ignore removed ones.
++
+For more details about the <pathspec> syntax, see the 'pathspec' entry
+in linkgit:gitglossary[7].
 
 -n::
 --dry-run::
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 d0b33587717a198655164f5dd5e35e116a8a30e0..fe029ac6fc1a2280893c8ad7c36582c3f891a79e 100644 (file)
@@ -18,6 +18,7 @@ SYNOPSIS
 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
 'git branch' --unset-upstream [<branchname>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
+'git branch' (-c | -C) [<oldbranch>] <newbranch>
 'git branch' (-d | -D) [-r] <branchname>...
 'git branch' --edit-description [<branchname>]
 
@@ -64,6 +65,10 @@ If <oldbranch> had a corresponding reflog, it is renamed to match
 renaming. If <newbranch> exists, -M must be used to force the rename
 to happen.
 
+The `-c` and `-C` options have the exact same semantics as `-m` and
+`-M`, except instead of the branch being renamed it along with its
+config and reflog will be copied to a new name.
+
 With a `-d` or `-D` option, `<branchname>` will be deleted.  You may
 specify more than one branch for deletion.  If the branch currently
 has a reflog then the reflog will also be deleted.
@@ -92,10 +97,10 @@ OPTIONS
        all changes made to the branch ref, enabling use of date
        based sha1 expressions such as "<branchname>@\{yesterday}".
        Note that in non-bare repositories, reflogs are usually
-       enabled by default by the `core.logallrefupdates` config option.
+       enabled by default by the `core.logAllRefUpdates` config option.
        The negated form `--no-create-reflog` only overrides an earlier
        `--create-reflog`, but currently does not negate the setting of
-       `core.logallrefupdates`.
+       `core.logAllRefUpdates`.
 
 -f::
 --force::
@@ -104,7 +109,7 @@ OPTIONS
        In combination with `-d` (or `--delete`), allow deleting the
        branch irrespective of its merged status. In combination with
        `-m` (or `--move`), allow renaming the branch even if the new
-       branch name already exists.
+       branch name already exists, the same applies for `-c` (or `--copy`).
 
 -m::
 --move::
@@ -113,6 +118,13 @@ OPTIONS
 -M::
        Shortcut for `--move --force`.
 
+-c::
+--copy::
+       Copy a branch and the corresponding reflog.
+
+-C::
+       Shortcut for `--copy --force`.
+
 --color[=<when>]::
        Color branches to highlight current, local, and
        remote-tracking branches.
@@ -195,10 +207,8 @@ start-point is either a local or remote-tracking branch.
        branch.autoSetupMerge configuration variable is true.
 
 --set-upstream::
-       If specified branch does not exist yet or if `--force` has been
-       given, acts exactly like `--track`. Otherwise sets up configuration
-       like `--track` would when creating the branch, except that where
-       branch points to is not changed.
+       As this option had confusing syntax, it is no longer supported.
+       Please use `--track` or `--set-upstream-to` instead.
 
 -u <upstream>::
 --set-upstream-to=<upstream>::
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 9e5169aa64f4ffd5ea42f85b2dbe812086e9652c..bebdcdec5ad4775588abe8814c59a269c7c1a832 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
        [--commit-filter <command>] [--tag-name-filter <command>]
        [--subdirectory-filter <directory>] [--prune-empty]
        [--original <namespace>] [-d <directory>] [-f | --force]
-       [--] [<rev-list options>...]
+       [--state-branch <branch>] [--] [<rev-list options>...]
 
 DESCRIPTION
 -----------
@@ -198,6 +198,12 @@ to other tags will be rewritten to point to the underlying commit.
        directory or when there are already refs starting with
        'refs/original/', unless forced.
 
+--state-branch <branch>::
+       This option will cause the mapping from old to new objects to
+       be loaded from named branch upon startup and saved as a new
+       commit to that branch upon exit, enabling incremental of large
+       trees. If '<branch>' does not exist it will be created.
+
 <rev-list options>...::
        Arguments for 'git rev-list'.  All positive refs included by
        these options are rewritten.  You may also specify options
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 720c7850e2790bf18a3c493977a2703cbeab048a..18b494731f51145e9d4a4d078264bedfd3beb00f 100644 (file)
@@ -289,6 +289,9 @@ providing this option will cause it to die.
 <pathspec>...::
        If given, limit the search to paths matching at least one pattern.
        Both leading paths match and glob(7) patterns are supported.
++
+For more details about the <pathspec> syntax, see the 'pathspec' entry
+in linkgit:gitglossary[7].
 
 Examples
 --------
@@ -305,6 +308,9 @@ Examples
        Looks for a line that has `NODE` or `Unexpected` in
        files that have lines that match both.
 
+`git grep solution -- :^Documentation`::
+       Looks for `solution`, excluding files in `Documentation`.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
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 02576d8c0ae6008fc22d7c7c706909efde5856da..72bd809fb8d59812d79e402f6b9608d09f0d52ec 100644 (file)
@@ -179,6 +179,7 @@ Here are the "carry forward" rules, where "I" denotes the index,
 "clean" means that index and work tree coincide, and "exists"/"nothing"
 refer to the presence of a path in the specified commit:
 
+....
        I                   H        M        Result
        -------------------------------------------------------
      0  nothing             nothing  nothing  (does not happen)
@@ -217,6 +218,7 @@ refer to the presence of a path in the specified commit:
      19 no    no    yes     exists   exists   keep index
      20 yes   yes   no      exists   exists   use M
      21 no    yes   no      exists   exists   fail
+....
 
 In all "keep index" cases, the index entry stays as in the
 original index file.  If the entry is not up to date,
index 4f6bed61a92b74e4540b71e0e0cac7cba60302ca..3cedfb0fd22be1c060ab2e7b9a141a0d1deb03af 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
@@ -430,13 +430,15 @@ without an explicit `--interactive`.
 --autosquash::
 --no-autosquash::
        When the commit log message begins with "squash! ..." (or
-       "fixup! ..."), and there is a commit whose title begins with
-       the same ..., automatically modify the todo list of rebase -i
-       so that the commit marked for squashing comes right after the
-       commit to be modified, and change the action of the moved
-       commit from `pick` to `squash` (or `fixup`).  Ignores subsequent
-       "fixup! " or "squash! " after the first, in case you referred to an
-       earlier fixup/squash with `git commit --fixup/--squash`.
+       "fixup! ..."), and there is already a commit in the todo list that
+       matches the same `...`, automatically modify the todo list of rebase
+       -i so that the commit marked for squashing comes right after the
+       commit to be modified, and change the action of the moved commit
+       from `pick` to `squash` (or `fixup`).  A commit matches the `...` if
+       the commit subject matches, or if the `...` refers to the commit's
+       hash. As a fall-back, partial matches of the commit subject work,
+       too.  The recommended way to create fixup/squash commits is by using
+       the `--fixup`/`--squash` options of linkgit:git-commit[1].
 +
 This option is only valid when the `--interactive` option is used.
 +
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 2e30a3e42d4e4e2bab580b0dcb5701ca3b33aeed..54cf2560bebbfc538644c636f696d4e500f1d852 100644 (file)
@@ -79,6 +79,22 @@ EOF
 $ chmod +x $HOME/git-shell-commands/no-interactive-login
 ----------------
 
+To enable git-cvsserver access (which should generally have the
+`no-interactive-login` example above as a prerequisite, as creating
+the git-shell-commands directory allows interactive logins):
+
+----------------
+$ cat >$HOME/git-shell-commands/cvs <<\EOF
+if ! test $# = 1 && test "$1" = "server"
+then
+       echo >&2 "git-cvsserver only handles \"server\""
+       exit 1
+fi
+exec git cvsserver server
+EOF
+$ chmod +x $HOME/git-shell-commands/cvs
+----------------
+
 SEE ALSO
 --------
 ssh(1),
index d47f198f15cd4c767b03c14580b816bf750e9ee1..9f3a78a36c48c55318ee0eea8e96a64ccce5bfa2 100644 (file)
@@ -111,6 +111,8 @@ configuration variable documented in linkgit:git-config[1].
        without options are equivalent to 'always' and 'never'
        respectively.
 
+<pathspec>...::
+       See the 'pathspec' entry in linkgit:gitglossary[7].
 
 OUTPUT
 ------
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 543fb425ee7c1d4c09a5ad4afbd00b9b812e4871..95e9f391d88fc434df6bc3f0483c9b3c80983021 100644 (file)
@@ -174,7 +174,7 @@ This option is only applicable when listing tags without annotation lines.
        `core.logAllRefUpdates` in linkgit:git-config[1].
        The negated form `--no-create-reflog` only overrides an earlier
        `--create-reflog`, but currently does not negate the setting of
-       `core.logallrefupdates`.
+       `core.logAllRefUpdates`.
 
 <tagname>::
        The name of the tag to create, delete, or describe.
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 6e3a6767e5f0ce347b2363cc7829d8eab042ae0a..f7e603bc826536b2d4179bf8fbee8f834d6031ae 100644 (file)
@@ -159,6 +159,10 @@ foo.bar= ...`) sets `foo.bar` to the empty string which ` git config
        Add "icase" magic to all pathspec. This is equivalent to setting
        the `GIT_ICASE_PATHSPECS` environment variable to `1`.
 
+--no-optional-locks::
+       Do not perform optional operations that require locks. This is
+       equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
+
 GIT COMMANDS
 ------------
 
@@ -697,6 +701,14 @@ of clones and fetches.
        which feed potentially-untrusted URLS to git commands.  See
        linkgit:git-config[1] for more details.
 
+`GIT_OPTIONAL_LOCKS`::
+       If set to `0`, Git will complete any requested operation without
+       performing any optional sub-operations that require taking a lock.
+       For example, this will prevent `git status` from refreshing the
+       index as a side effect. This is useful for processes running in
+       the background which do not want to cause lock contention with
+       other operations on the repository.  Defaults to `1`.
+
 Discussion[[Discussion]]
 ------------------------
 
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 623ed1a13829188971531aa62c6e109e9d29ec44..5d3f45560ea9c29ec382c8772522f06ab5f81f73 100644 (file)
@@ -127,11 +127,10 @@ help message found in the commented portion of the commit template.
 commit-msg
 ~~~~~~~~~~
 
-This hook is invoked by 'git commit', and can be bypassed
-with the `--no-verify` option.  It takes a single parameter, the
-name of the file that holds the proposed commit log message.
-Exiting with a non-zero status causes the 'git commit' to
-abort.
+This hook is invoked by 'git commit' and 'git merge', and can be
+bypassed with the `--no-verify` option.  It takes a single parameter,
+the name of the file that holds the proposed commit log message.
+Exiting with a non-zero status causes the command to abort.
 
 The hook is allowed to edit the message file in place, and can be used
 to normalize the message into some project standard format. It
@@ -369,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 b71b943b12eda2eab1792dfc910e405ad2ad540c..6b8888d123826179ace38660f5043d897eb5ce70 100644 (file)
@@ -407,7 +407,7 @@ these forms:
 
 exclude;;
        After a path matches any non-exclude pathspec, it will be run
-       through all exclude pathspec (magic signature: `!` or its
+       through all exclude pathspecs (magic signature: `!` or its
        synonym `^`). If it matches, the path is ignored.  When there
        is no non-exclude pathspec, the exclusion is applied to the
        result set as if invoked without any pathspec.
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 2eb92b93274df9fb5002336114654fe869903808..a09d5974639fa772ba86c5547fe70716fd044f73 100644 (file)
@@ -39,7 +39,8 @@ even look at what the other tree contains at all.  It discards everything
 the other tree did, declaring 'our' history contains all that happened in it.
 
 theirs;;
-       This is the opposite of 'ours'.
+       This is the opposite of 'ours'; note that, unlike 'ours', there is
+       no 'theirs' merge stragegy to confuse this merge option with.
 
 patience;;
        With this option, 'merge-recursive' spends a little extra time
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.
 
diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt
deleted file mode 100644 (file)
index c08402b..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-string-list API
-===============
-
-The string_list API offers a data structure and functions to handle
-sorted and unsorted string lists.  A "sorted" list is one whose
-entries are sorted by string value in `strcmp()` order.
-
-The 'string_list' struct used to be called 'path_list', but was renamed
-because it is not specific to paths.
-
-The caller:
-
-. Allocates and clears a `struct string_list` variable.
-
-. Initializes the members. You might want to set the flag `strdup_strings`
-  if the strings should be strdup()ed. For example, this is necessary
-  when you add something like git_path("..."), since that function returns
-  a static buffer that will change with the next call to git_path().
-+
-If you need something advanced, you can manually malloc() the `items`
-member (you need this if you add things later) and you should set the
-`nr` and `alloc` members in that case, too.
-
-. Adds new items to the list, using `string_list_append`,
-  `string_list_append_nodup`, `string_list_insert`,
-  `string_list_split`, and/or `string_list_split_in_place`.
-
-. Can check if a string is in the list using `string_list_has_string` or
-  `unsorted_string_list_has_string` and get it from the list using
-  `string_list_lookup` for sorted lists.
-
-. Can sort an unsorted list using `string_list_sort`.
-
-. Can remove duplicate items from a sorted list using
-  `string_list_remove_duplicates`.
-
-. Can remove individual items of an unsorted list using
-  `unsorted_string_list_delete_item`.
-
-. Can remove items not matching a criterion from a sorted or unsorted
-  list using `filter_string_list`, or remove empty strings using
-  `string_list_remove_empty_items`.
-
-. Finally it should free the list using `string_list_clear`.
-
-Example:
-
-----
-struct string_list list = STRING_LIST_INIT_NODUP;
-int i;
-
-string_list_append(&list, "foo");
-string_list_append(&list, "bar");
-for (i = 0; i < list.nr; i++)
-       printf("%s\n", list.items[i].string)
-----
-
-NOTE: It is more efficient to build an unsorted list and sort it
-afterwards, instead of building a sorted list (`O(n log n)` instead of
-`O(n^2)`).
-+
-However, if you use the list to check if a certain string was added
-already, you should not do that (using unsorted_string_list_has_string()),
-because the complexity would be quadratic again (but with a worse factor).
-
-Functions
----------
-
-* General ones (works with sorted and unsorted lists as well)
-
-`string_list_init`::
-
-       Initialize the members of the string_list, set `strdup_strings`
-       member according to the value of the second parameter.
-
-`filter_string_list`::
-
-       Apply a function to each item in a list, retaining only the
-       items for which the function returns true.  If free_util is
-       true, call free() on the util members of any items that have
-       to be deleted.  Preserve the order of the items that are
-       retained.
-
-`string_list_remove_empty_items`::
-
-       Remove any empty strings from the list.  If free_util is true,
-       call free() on the util members of any items that have to be
-       deleted.  Preserve the order of the items that are retained.
-
-`print_string_list`::
-
-       Dump a string_list to stdout, useful mainly for debugging purposes. It
-       can take an optional header argument and it writes out the
-       string-pointer pairs of the string_list, each one in its own line.
-
-`string_list_clear`::
-
-       Free a string_list. The `string` pointer of the items will be freed in
-       case the `strdup_strings` member of the string_list is set. The second
-       parameter controls if the `util` pointer of the items should be freed
-       or not.
-
-* Functions for sorted lists only
-
-`string_list_has_string`::
-
-       Determine if the string_list has a given string or not.
-
-`string_list_insert`::
-
-       Insert a new element to the string_list. The returned pointer can be
-       handy if you want to write something to the `util` pointer of the
-       string_list_item containing the just added string. If the given
-       string already exists the insertion will be skipped and the
-       pointer to the existing item returned.
-+
-Since this function uses xrealloc() (which die()s if it fails) if the
-list needs to grow, it is safe not to check the pointer. I.e. you may
-write `string_list_insert(...)->util = ...;`.
-
-`string_list_lookup`::
-
-       Look up a given string in the string_list, returning the containing
-       string_list_item. If the string is not found, NULL is returned.
-
-`string_list_remove_duplicates`::
-
-       Remove all but the first of consecutive entries that have the
-       same string value.  If free_util is true, call free() on the
-       util members of any items that have to be deleted.
-
-* Functions for unsorted lists only
-
-`string_list_append`::
-
-       Append a new string to the end of the string_list.  If
-       `strdup_string` is set, then the string argument is copied;
-       otherwise the new `string_list_entry` refers to the input
-       string.
-
-`string_list_append_nodup`::
-
-       Append a new string to the end of the string_list.  The new
-       `string_list_entry` always refers to the input string, even if
-       `strdup_string` is set.  This function can be used to hand
-       ownership of a malloc()ed string to a `string_list` that has
-       `strdup_string` set.
-
-`string_list_sort`::
-
-       Sort the list's entries by string value in `strcmp()` order.
-
-`unsorted_string_list_has_string`::
-
-       It's like `string_list_has_string()` but for unsorted lists.
-
-`unsorted_string_list_lookup`::
-
-       It's like `string_list_lookup()` but for unsorted lists.
-+
-The above two functions need to look through all items, as opposed to their
-counterpart for sorted lists, which performs a binary search.
-
-`unsorted_string_list_delete_item`::
-
-       Remove an item from a string_list. The `string` pointer of the items
-       will be freed in case the `strdup_strings` member of the string_list
-       is set. The third parameter controls if the `util` pointer of the
-       items should be freed or not.
-
-`string_list_split`::
-`string_list_split_in_place`::
-
-       Split a string into substrings on a delimiter character and
-       append the substrings to a `string_list`.  If `maxsplit` is
-       non-negative, then split at most `maxsplit` times.  Return the
-       number of substrings appended to the list.
-+
-`string_list_split` requires a `string_list` that has `strdup_strings`
-set to true; it leaves the input string untouched and makes copies of
-the substrings in newly-allocated memory.
-`string_list_split_in_place` requires a `string_list` that has
-`strdup_strings` set to false; it splits the input string in place,
-overwriting the delimiter characters with NULs and creating new
-string_list_items that point into the original string (the original
-string must therefore not be modified or freed while the `string_list`
-is in use).
-
-
-Data structures
----------------
-
-* `struct string_list_item`
-
-Represents an item of the list. The `string` member is a pointer to the
-string, and you may use the `util` member for any purpose, if you want.
-
-* `struct string_list`
-
-Represents the list itself.
-
-. The array of items are available via the `items` member.
-. The `nr` member contains the number of items stored in the list.
-. The `alloc` member is used to avoid reallocating at every insertion.
-  You should not tamper with it.
-. Setting the `strdup_strings` member to 1 will strdup() the strings
-  before adding them, see above.
-. The `compare_strings_fn` member is used to specify a custom compare
-  function, otherwise `strcmp()` is used as the default function.
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 d34c3dfe69d8b02db7653627400792f92aa98afe..b143e4eea3fa75eebd883361c40c3faa9e6c8060 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
@@ -1039,6 +1044,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
@@ -1481,6 +1489,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
@@ -1490,17 +1507,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
@@ -2448,6 +2463,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 \
index 708aff8d42c562112fe4076635af903c7f41fbaa..98579853299427ff906434fe56288a73b9d19b21 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -202,6 +202,10 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
        return retval;
 }
 
+/*
+ * Resolve `path` into an absolute, cleaned-up path. The return value
+ * comes from a shared buffer.
+ */
 const char *real_path(const char *path)
 {
        static struct strbuf realpath = STRBUF_INIT;
diff --git a/apply.c b/apply.c
index 86666217d4df0c8162c8d93d859ab5112c10f4b9..c022af53a24bf56556d136a3456862cf6a2ca7cc 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;
 }
 
 /*
@@ -2921,6 +2920,7 @@ static int apply_one_fragment(struct apply_state *state,
                        if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
                            ws_blank_line(patch + 1, plen, ws_rule))
                                is_blank_context = 1;
+                       /* fallthrough */
                case '-':
                        memcpy(old, patch + 1, plen);
                        add_line_info(&preimage, old, plen,
@@ -2928,7 +2928,7 @@ static int apply_one_fragment(struct apply_state *state,
                        old += plen;
                        if (first == '-')
                                break;
-               /* Fall-through for ' ' */
+                       /* fallthrough */
                case '+':
                        /* --no-add does not add new lines */
                        if (first == '+' && state->no_add)
index 557dd2db85ff261ee00ec50ea0bfe286bff7bda3..1e41f4bbeb1d8730da83aad12bc8987abedcad8a 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -103,17 +103,34 @@ struct archiver_context {
        struct directory *bottom;
 };
 
+static const struct attr_check *get_archive_attrs(const char *path)
+{
+       static struct attr_check *check;
+       if (!check)
+               check = attr_check_initl("export-ignore", "export-subst", NULL);
+       return git_check_attr(path, check) ? NULL : check;
+}
+
+static int check_attr_export_ignore(const struct attr_check *check)
+{
+       return check && ATTR_TRUE(check->items[0].value);
+}
+
+static int check_attr_export_subst(const struct attr_check *check)
+{
+       return check && ATTR_TRUE(check->items[1].value);
+}
+
 static int write_archive_entry(const unsigned char *sha1, const char *base,
                int baselen, const char *filename, unsigned mode, int stage,
                void *context)
 {
        static struct strbuf path = STRBUF_INIT;
-       static struct attr_check *check;
        struct archiver_context *c = context;
        struct archiver_args *args = c->args;
        write_archive_entry_fn_t write_entry = c->write_entry;
-       const char *path_without_prefix;
        int err;
+       const char *path_without_prefix;
 
        args->convert = 0;
        strbuf_reset(&path);
@@ -125,12 +142,12 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                strbuf_addch(&path, '/');
        path_without_prefix = path.buf + args->baselen;
 
-       if (!check)
-               check = attr_check_initl("export-ignore", "export-subst", NULL);
-       if (!git_check_attr(path_without_prefix, check)) {
-               if (ATTR_TRUE(check->items[0].value))
+       if (!S_ISDIR(mode)) {
+               const struct attr_check *check;
+               check = get_archive_attrs(path_without_prefix);
+               if (check_attr_export_ignore(check))
                        return 0;
-               args->convert = ATTR_TRUE(check->items[1].value);
+               args->convert = check_attr_export_subst(check);
        }
 
        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
@@ -147,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)
@@ -204,6 +213,17 @@ static int queue_or_write_archive_entry(const unsigned char *sha1,
        }
 
        if (S_ISDIR(mode)) {
+               size_t baselen = base->len;
+               const struct attr_check *check;
+
+               /* Borrow base, but restore its original value when done. */
+               strbuf_addstr(base, filename);
+               strbuf_addch(base, '/');
+               check = get_archive_attrs(base->buf);
+               strbuf_setlen(base, baselen);
+
+               if (check_attr_export_ignore(check))
+                       return 0;
                queue_directory(sha1, base, filename,
                                mode, stage, c);
                return READ_TREE_RECURSIVE;
@@ -257,9 +277,7 @@ int write_archive_entries(struct archiver_args *args,
        }
 
        err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
-                                 args->pathspec.has_wildcard ?
-                                 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 2549eaf7b15152a9c130e63e44c8d8f4fc865233..96beeb5d13630672fba3021468670a1bcfc72158 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -826,7 +826,8 @@ static int check_ancestors(const char *prefix)
 
        /* Clean up objects used, as they will be reused. */
        clear_commit_marks_for_object_array(&pending_copy, ALL_REV_FLAGS);
-       free(pending_copy.objects);
+
+       object_array_clear(&pending_copy);
 
        return res;
 }
index 36541d05cd7b934bceba1af4fd4c121e51cd862b..4377ce2fb17d7467ee92d4db02cb4859f6397505 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -90,24 +90,24 @@ int install_branch_config(int flag, const char *local, const char *origin, const
                if (shortname) {
                        if (origin)
                                printf_ln(rebasing ?
-                                         _("Branch %s set up to track remote branch %s from %s by rebasing.") :
-                                         _("Branch %s set up to track remote branch %s from %s."),
+                                         _("Branch '%s' set up to track remote branch '%s' from '%s' by rebasing.") :
+                                         _("Branch '%s' set up to track remote branch '%s' from '%s'."),
                                          local, shortname, origin);
                        else
                                printf_ln(rebasing ?
-                                         _("Branch %s set up to track local branch %s by rebasing.") :
-                                         _("Branch %s set up to track local branch %s."),
+                                         _("Branch '%s' set up to track local branch '%s' by rebasing.") :
+                                         _("Branch '%s' set up to track local branch '%s'."),
                                          local, shortname);
                } else {
                        if (origin)
                                printf_ln(rebasing ?
-                                         _("Branch %s set up to track remote ref %s by rebasing.") :
-                                         _("Branch %s set up to track remote ref %s."),
+                                         _("Branch '%s' set up to track remote ref '%s' by rebasing.") :
+                                         _("Branch '%s' set up to track remote ref '%s'."),
                                          local, remote);
                        else
                                printf_ln(rebasing ?
-                                         _("Branch %s set up to track local ref %s by rebasing.") :
-                                         _("Branch %s set up to track local ref %s."),
+                                         _("Branch '%s' set up to track local ref '%s' by rebasing.") :
+                                         _("Branch '%s' set up to track local ref '%s'."),
                                          local, remote);
                }
        }
@@ -191,9 +191,8 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
 
        if (!attr_only) {
                const char *head;
-               struct object_id oid;
 
-               head = resolve_ref_unsafe("HEAD", 0, oid.hash, NULL);
+               head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
                if (!is_bare_repository() && head && !strcmp(head, ref->buf))
                        die(_("Cannot force update the current branch."));
        }
@@ -357,8 +356,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 e0daf17548d53867b383c676adee5ed901f56c15..67adaef4d80ee4e2fcd28755255935f0fcae6c03 100644 (file)
@@ -488,7 +488,7 @@ static int read_ancestry(const char *graft_file)
                return -1;
        while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
-               struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
+               struct commit_graft *graft = read_graft_line(&buf);
                if (graft)
                        register_commit_graft(graft, 0);
        }
index 16d391b407c9fa688b48a3e28f77800dcc1e2e0c..b998e16d0c24b300ce1469749e290c7879af14a7 100644 (file)
@@ -28,6 +28,7 @@ static const char * const builtin_branch_usage[] = {
        N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"),
        N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
        N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
+       N_("git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"),
        N_("git branch [<options>] [-r | -a] [--points-at]"),
        N_("git branch [<options>] [-r | -a] [--format]"),
        NULL
@@ -456,15 +457,19 @@ static void reject_rebase_or_bisect_branch(const char *target)
        free_worktrees(worktrees);
 }
 
-static void rename_branch(const char *oldname, const char *newname, int force)
+static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
 {
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
        struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
        int recovery = 0;
        int clobber_head_ok;
 
-       if (!oldname)
-               die(_("cannot rename the current branch while not on any."));
+       if (!oldname) {
+               if (copy)
+                       die(_("cannot copy the current branch while not on any."));
+               else
+                       die(_("cannot rename the current branch while not on any."));
+       }
 
        if (strbuf_check_branch_ref(&oldref, oldname)) {
                /*
@@ -487,16 +492,29 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 
        reject_rebase_or_bisect_branch(oldref.buf);
 
-       strbuf_addf(&logmsg, "Branch: renamed %s to %s",
-                oldref.buf, newref.buf);
+       if (copy)
+               strbuf_addf(&logmsg, "Branch: copied %s to %s",
+                           oldref.buf, newref.buf);
+       else
+               strbuf_addf(&logmsg, "Branch: renamed %s to %s",
+                           oldref.buf, newref.buf);
 
-       if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
+       if (!copy && rename_ref(oldref.buf, newref.buf, logmsg.buf))
                die(_("Branch rename failed"));
+       if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
+               die(_("Branch copy failed"));
 
-       if (recovery)
-               warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
+       if (recovery) {
+               if (copy)
+                       warning(_("Copied a misnamed branch '%s' away"),
+                               oldref.buf + 11);
+               else
+                       warning(_("Renamed a misnamed branch '%s' away"),
+                               oldref.buf + 11);
+       }
 
-       if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
+       if (!copy &&
+           replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
                die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
 
        strbuf_release(&logmsg);
@@ -505,8 +523,10 @@ static void rename_branch(const char *oldname, const char *newname, int force)
        strbuf_release(&oldref);
        strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
        strbuf_release(&newref);
-       if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
+       if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
                die(_("Branch is renamed, but update of config-file failed"));
+       if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
+               die(_("Branch is copied, but update of config-file failed"));
        strbuf_release(&oldsection);
        strbuf_release(&newsection);
 }
@@ -544,7 +564,7 @@ static int edit_branch_description(const char *branch_name)
 
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
-       int delete = 0, rename = 0, force = 0, list = 0;
+       int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
        int reflog = 0, edit_description = 0;
        int quiet = 0, unset_upstream = 0;
        const char *new_upstream = NULL;
@@ -561,8 +581,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT__QUIET(&quiet, N_("suppress informational messages")),
                OPT_SET_INT('t', "track",  &track, N_("set up tracking mode (see git-pull(1))"),
                        BRANCH_TRACK_EXPLICIT),
-               OPT_SET_INT( 0, "set-upstream",  &track, N_("change upstream info"),
-                       BRANCH_TRACK_OVERRIDE),
+               { OPTION_SET_INT, 0, "set-upstream", &track, NULL, N_("do not use"),
+                       PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, BRANCH_TRACK_OVERRIDE },
                OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
                OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("Unset the upstream info")),
                OPT__COLOR(&branch_use_color, N_("use colored output")),
@@ -581,6 +601,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
                OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
                OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+               OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
+               OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
                OPT_BOOL(0, "list", &list, N_("list branch names")),
                OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")),
                OPT_BOOL(0, "edit-description", &edit_description,
@@ -624,14 +646,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
                             0);
 
-       if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
+       if (!delete && !rename && !copy && !edit_description && !new_upstream && !unset_upstream && argc == 0)
                list = 1;
 
        if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
            filter.no_commit)
                list = 1;
 
-       if (!!delete + !!rename + !!new_upstream +
+       if (!!delete + !!rename + !!copy + !!new_upstream +
            list + unset_upstream > 1)
                usage_with_options(builtin_branch_usage, options);
 
@@ -649,6 +671,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (force) {
                delete *= 2;
                rename *= 2;
+               copy *= 2;
        }
 
        if (delete) {
@@ -703,13 +726,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
                if (edit_branch_description(branch_name))
                        return 1;
+       } else if (copy) {
+               if (!argc)
+                       die(_("branch name required"));
+               else if (argc == 1)
+                       copy_or_rename_branch(head, argv[0], 1, copy > 1);
+               else if (argc == 2)
+                       copy_or_rename_branch(argv[0], argv[1], 1, copy > 1);
+               else
+                       die(_("too many branches for a copy operation"));
        } else if (rename) {
                if (!argc)
                        die(_("branch name required"));
                else if (argc == 1)
-                       rename_branch(head, argv[0], rename > 1);
+                       copy_or_rename_branch(head, argv[0], 0, rename > 1);
                else if (argc == 2)
-                       rename_branch(argv[0], argv[1], rename > 1);
+                       copy_or_rename_branch(argv[0], argv[1], 0, rename > 1);
                else
                        die(_("too many branches for a rename operation"));
        } else if (new_upstream) {
@@ -759,8 +791,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        } else if (argc > 0 && argc <= 2) {
                struct branch *branch = branch_get(argv[0]);
-               int branch_existed = 0, remote_tracking = 0;
-               struct strbuf buf = STRBUF_INIT;
 
                if (!strcmp(argv[0], "HEAD"))
                        die(_("it does not make sense to create 'HEAD' manually"));
@@ -772,28 +802,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
 
                if (track == BRANCH_TRACK_OVERRIDE)
-                       fprintf(stderr, _("The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to\n"));
-
-               strbuf_addf(&buf, "refs/remotes/%s", branch->name);
-               remote_tracking = ref_exists(buf.buf);
-               strbuf_release(&buf);
+                       die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
 
-               branch_existed = ref_exists(branch->refname);
                create_branch(argv[0], (argc == 2) ? argv[1] : head,
                              force, reflog, 0, quiet, track);
 
-               /*
-                * We only show the instructions if the user gave us
-                * one branch which doesn't exist locally, but is the
-                * name of a remote-tracking branch.
-                */
-               if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
-                   !branch_existed && remote_tracking) {
-                       fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
-                       fprintf(stderr, "    git branch -d %s\n", branch->name);
-                       fprintf(stderr, "    git branch --set-upstream-to %s\n", branch->name);
-               }
-
        } else
                usage_with_options(builtin_branch_usage, options);
 
index 4ccbfaac3148287535aa8833d0d6cbdacb7c1d3d..f5fa4fd75af26a66cd1b2c0d493116704bc16dd5 100644 (file)
@@ -97,7 +97,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                return !has_object_file(&oid);
 
        case 'w':
-               if (!path[0])
+               if (!path)
                        die("git cat-file --filters %s: <object> must be "
                            "<sha1:path>", obj_name);
 
@@ -107,12 +107,13 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                break;
 
        case 'c':
-               if (!path[0])
+               if (!path)
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
 
                if (textconv_object(path, obj_context.mode, &oid, 1, &buf, &size))
                        break;
+               /* else fallthrough */
 
        case 'p':
                type = sha1_object_info(oid.hash, NULL);
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 5c202b7af57e26f5171eb8a83b107fceeb747488..3345a0d16f21c38fe5fe6f32fa5b0a139e21c4a3 100644 (file)
@@ -436,6 +436,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
                 * update paths in the work tree, and we cannot revert
                 * them.
                 */
+               /* fallthrough */
        case 0:
                return 0;
        default:
@@ -796,9 +797,14 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
        for_each_ref(add_pending_uninteresting_ref, &revs);
        add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
 
+       /* Save pending objects, so they can be cleaned up later. */
        refs = revs.pending;
        revs.leak_pending = 1;
 
+       /*
+        * prepare_revision_walk (together with .leak_pending = 1) makes us
+        * the sole owner of the list of pending objects.
+        */
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
        if (!(old->object.flags & UNINTERESTING))
@@ -806,8 +812,10 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
        else
                describe_detached_head(_("Previous HEAD position was"), old);
 
+       /* Clean up objects used, as they will be reused. */
        clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
-       free(refs.objects);
+
+       object_array_clear(&refs);
 }
 
 static int switch_branches(const struct checkout_opts *opts,
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..d75b3805ea7fe3475564f337bf660d8155909445 100644 (file)
@@ -335,7 +335,7 @@ static void refresh_cache_or_die(int refresh_flags)
 static const char *prepare_index(int argc, const char **argv, const char *prefix,
                                 const struct commit *current_head, int is_status)
 {
-       struct string_list partial;
+       struct string_list partial = STRING_LIST_INIT_DUP;
        struct pathspec pathspec;
        int refresh_flags = REFRESH_QUIET;
        const char *ret;
@@ -380,7 +380,8 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                        warning(_("Failed to update main cache tree"));
 
                commit_style = COMMIT_NORMAL;
-               return get_lock_file_path(&index_lock);
+               ret = get_lock_file_path(&index_lock);
+               goto out;
        }
 
        /*
@@ -403,7 +404,8 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
-               return get_lock_file_path(&index_lock);
+               ret = get_lock_file_path(&index_lock);
+               goto out;
        }
 
        /*
@@ -429,7 +431,8 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                        rollback_lock_file(&index_lock);
                }
                commit_style = COMMIT_AS_IS;
-               return get_index_file();
+               ret = get_index_file();
+               goto out;
        }
 
        /*
@@ -460,7 +463,6 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                        die(_("cannot do a partial commit during a cherry-pick."));
        }
 
-       string_list_init(&partial, 1);
        if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
                exit(1);
 
@@ -490,6 +492,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
        discard_cache();
        ret = get_lock_file_path(&false_lock);
        read_cache_from(ret);
+out:
+       string_list_clear(&partial, 0);
+       clear_pathspec(&pathspec);
        return ret;
 }
 
@@ -1387,7 +1392,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        read_cache_preload(&s.pathspec);
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
 
-       fd = hold_locked_index(&index_lock, 0);
+       if (use_optional_locks())
+               fd = hold_locked_index(&index_lock, 0);
+       else
+               fd = -1;
 
        s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
        if (!s.is_initial)
@@ -1431,7 +1439,6 @@ static void print_summary(const char *prefix, const struct object_id *oid,
        struct rev_info rev;
        struct commit *commit;
        struct strbuf format = STRBUF_INIT;
-       struct object_id junk_oid;
        const char *head;
        struct pretty_print_context pctx = {0};
        struct strbuf author_ident = STRBUF_INIT;
@@ -1484,7 +1491,7 @@ static void print_summary(const char *prefix, const struct object_id *oid,
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
 
-       head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL);
+       head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
        if (!strcmp(head, "HEAD"))
                head = _("detached HEAD");
        else
@@ -1818,6 +1825,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..2fb60d6d48eae68affdcf64f6dbab0282408f393 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,15 +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;
+               commit = (struct commit *)object_array_pop(commits);
                if (has_unshown_parent(commit))
                        return;
-               handle_commit(commit, revs);
-               commits->nr--;
+               handle_commit(commit, revs, paths_of_changed_objects);
        }
 }
 
@@ -977,6 +992,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 +1065,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 1e4c471b4141e6ec350c4caf58bacafb6ac07b7c..56afe405b8072b3099a23661ac586f673e54ff0e 100644 (file)
@@ -182,12 +182,7 @@ static int traverse_reachable(void)
        if (show_progress)
                progress = start_delayed_progress(_("Checking connectivity"), 0);
        while (pending.nr) {
-               struct object_array_entry *entry;
-               struct object *obj;
-
-               entry = pending.objects + --pending.nr;
-               obj = entry->item;
-               result |= traverse_one_object(obj);
+               result |= traverse_one_object(object_array_pop(&pending));
                display_progress(progress, ++nr);
        }
        stop_progress(&progress);
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..2706fcfaf2261e2ac2eaff5a054d0dd7a9291c0b 100644 (file)
@@ -26,15 +26,16 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
                usage(builtin_get_tar_commit_id_usage);
 
        n = read_in_full(0, buffer, HEADERSIZE);
-       if (n < HEADERSIZE)
-               die("git get-tar-commit-id: read error");
+       if (n < 0)
+               die_errno("git get-tar-commit-id: read error");
+       if (n != HEADERSIZE)
+               die_errno("git get-tar-commit-id: EOF before reading tar header");
        if (header->typeflag[0] != 'g')
                return 1;
        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 f8cccbc96403a791ff9d4641bac1c30b621c0d5e..d81a09051ead0bb14161c25ef30bf8b0348b3198 100644 (file)
@@ -1660,10 +1660,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        check_head = 1;
 
                if (check_head) {
-                       struct object_id oid;
                        const char *ref, *v;
                        ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-                                                oid.hash, NULL);
+                                                NULL, NULL);
                        if (ref && skip_prefix(ref, "refs/heads/", &v))
                                branch_name = xstrdup(v);
                        else
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 7df3fe3927ef8a1c65f7baad7d957602436315cd..ab5ffe85e8f5d7a359004f2bbb52a5778d0b91b4 100644 (file)
@@ -73,6 +73,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
+static int verify_msg = 1;
 
 static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -236,6 +237,7 @@ static struct option builtin_merge_options[] = {
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
        OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
        OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
+       OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")),
        OPT_END()
 };
 
@@ -253,6 +255,7 @@ static int save_state(struct object_id *stash)
        struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf buffer = STRBUF_INIT;
        const char *argv[] = {"stash", "create", NULL};
+       int rc = -1;
 
        cp.argv = argv;
        cp.out = -1;
@@ -266,11 +269,14 @@ static int save_state(struct object_id *stash)
        if (finish_command(&cp) || len < 0)
                die(_("stash failed"));
        else if (!len)          /* no changes */
-               return -1;
+               goto out;
        strbuf_setlen(&buffer, buffer.len-1);
        if (get_oid(buffer.buf, stash))
                die(_("not a valid object: %s"), buffer.buf);
-       return 0;
+       rc = 0;
+out:
+       strbuf_release(&buffer);
+       return rc;
 }
 
 static void read_empty(unsigned const char *sha1, int verbose)
@@ -780,6 +786,12 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                if (launch_editor(git_path_merge_msg(), NULL, NULL))
                        abort_commit(remoteheads, NULL);
        }
+
+       if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
+                                         "commit-msg",
+                                         git_path_merge_msg(), NULL))
+               abort_commit(remoteheads, NULL);
+
        read_merge_msg(&msg);
        strbuf_stripspace(&msg, 0 < option_edit);
        if (!msg.len)
@@ -934,6 +946,7 @@ static void write_merge_heads(struct commit_list *remoteheads)
        if (fast_forward == FF_NO)
                strbuf_addstr(&buf, "no-ff");
        write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
+       strbuf_release(&buf);
 }
 
 static void write_merge_state(struct commit_list *remoteheads)
@@ -1359,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 &&
@@ -1442,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..5ee2c48ffb82e4adde3fe25b4661f3624314f238 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;
 }
@@ -2557,8 +2563,8 @@ struct in_pack_object {
 };
 
 struct in_pack {
-       int alloc;
-       int nr;
+       unsigned int alloc;
+       unsigned int nr;
        struct in_pack_object *array;
 };
 
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 c82b4dce6838fa039e9c2cfc769c7cd17a8ef562..f8519363a393862b6857acab037e74367c7f2134 100644 (file)
@@ -12,15 +12,30 @@ static const char * const builtin_rebase_helper_usage[] = {
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
        struct replay_opts opts = REPLAY_OPTS_INIT;
+       int keep_empty = 0;
        enum {
-               CONTINUE = 1, ABORT
+               CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_SHA1S, EXPAND_SHA1S,
+               CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH
        } command = 0;
        struct option options[] = {
                OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
+               OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
                OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
                                CONTINUE),
                OPT_CMDMODE(0, "abort", &command, N_("abort rebase"),
                                ABORT),
+               OPT_CMDMODE(0, "make-script", &command,
+                       N_("make rebase script"), MAKE_SCRIPT),
+               OPT_CMDMODE(0, "shorten-ids", &command,
+                       N_("shorten SHA-1s in the todo list"), SHORTEN_SHA1S),
+               OPT_CMDMODE(0, "expand-ids", &command,
+                       N_("expand SHA-1s in the todo list"), EXPAND_SHA1S),
+               OPT_CMDMODE(0, "check-todo-list", &command,
+                       N_("check the todo list"), CHECK_TODO_LIST),
+               OPT_CMDMODE(0, "skip-unnecessary-picks", &command,
+                       N_("skip unnecessary picks"), SKIP_UNNECESSARY_PICKS),
+               OPT_CMDMODE(0, "rearrange-squash", &command,
+                       N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
                OPT_END()
        };
 
@@ -37,5 +52,17 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
                return !!sequencer_continue(&opts);
        if (command == ABORT && argc == 1)
                return !!sequencer_remove_state(&opts);
+       if (command == MAKE_SCRIPT && argc > 1)
+               return !!sequencer_make_script(keep_empty, stdout, argc, argv);
+       if (command == SHORTEN_SHA1S && argc == 1)
+               return !!transform_todo_ids(1);
+       if (command == EXPAND_SHA1S && argc == 1)
+               return !!transform_todo_ids(0);
+       if (command == CHECK_TODO_LIST && argc == 1)
+               return !!check_todo_list();
+       if (command == SKIP_UNNECESSARY_PICKS && argc == 1)
+               return !!skip_unnecessary_picks();
+       if (command == REARRANGE_SQUASH && argc == 1)
+               return !!rearrange_squash();
        usage_with_options(builtin_rebase_helper_usage, options);
 }
index 52c63ebfdc737688a7d99f6be34c43cf80b1d011..29a0f3b75fb02dfa2a02c325816453e36baaa55a 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);
@@ -1207,11 +1207,10 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
        const char *dst_name;
        struct string_list_item *item;
        struct command *dst_cmd;
-       unsigned char sha1[GIT_MAX_RAWSZ];
        int flag;
 
        strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
-       dst_name = resolve_ref_unsafe(buf.buf, 0, sha1, &flag);
+       dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
        strbuf_release(&buf);
 
        if (!(flag & REF_ISSYMREF))
index e237d927a0881b56135a21a1b0041e7de6ff39ab..2067cca5b1ec4ed141a3e404cb7738c9b0ebc5d9 100644 (file)
@@ -126,7 +126,7 @@ static int commit_is_complete(struct commit *commit)
                struct commit *c;
                struct commit_list *parent;
 
-               c = (struct commit *)study.objects[--study.nr].item;
+               c = (struct commit *)object_array_pop(&study);
                if (!c->object.parsed && !parse_object(&c->object.oid))
                        c->object.flags |= INCOMPLETE;
 
@@ -182,8 +182,8 @@ static int commit_is_complete(struct commit *commit)
                        found.objects[i].item->flags |= SEEN;
        }
        /* free object arrays */
-       free(study.objects);
-       free(found.objects);
+       object_array_clear(&study);
+       object_array_clear(&found);
        return !is_incomplete;
 }
 
index bfb21ba7d217bafaf3527af007cd4ac4658d36c5..6a9127a33c019c5e6336075b5f68c7514d720b04 100644 (file)
@@ -57,7 +57,7 @@ static char *strip_escapes(const char *str, const char *service,
                                special = str[rpos];
                                if (rpos == 1)
                                        break;
-                               /* Fall-through to error. */
+                               /* fallthrough */
                        default:
                                die("Bad remote-ext placeholder '%%%c'.",
                                        str[rpos]);
index a995ea86c17474be248a974469f7d535c942fc70..4f5cac96b0f5e0044ddf0fd1e2ea686f694d62ca 100644 (file)
@@ -558,19 +558,19 @@ static int read_remote_branches(const char *refname,
        struct strbuf buf = STRBUF_INIT;
        struct string_list_item *item;
        int flag;
-       struct object_id orig_oid;
        const char *symref;
 
        strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
        if (starts_with(refname, buf.buf)) {
                item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
-                                           orig_oid.hash, &flag);
+                                           NULL, &flag);
                if (flag & REF_ISSYMREF)
                        item->util = xstrdup(symref);
                else
                        item->util = NULL;
        }
+       strbuf_release(&buf);
 
        return 0;
 }
@@ -595,6 +595,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 +1564,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 +1587,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 818fe74f0af8696fe21a75d4aaf6b919b645d22b..06ed02f99402be6a7edb36ff0eb08b903c513c6c 100644 (file)
@@ -17,9 +17,8 @@
 static char *get_default_remote(void)
 {
        char *dest = NULL, *ret;
-       unsigned char sha1[20];
        struct strbuf sb = STRBUF_INIT;
-       const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+       const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
 
        if (!refname)
                die(_("No such ref: %s"), "HEAD");
@@ -1089,8 +1088,7 @@ static const char *remote_submodule_branch(const char *path)
                return "master";
 
        if (!strcmp(branch, ".")) {
-               unsigned char sha1[20];
-               const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+               const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
 
                if (!refname)
                        die(_("No such ref: %s"), "HEAD");
@@ -1189,6 +1187,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
                                                break;
                                        die("HEAD does not match the named branch in the superproject");
                                }
+                               /* fallthrough */
                        default:
                                die("src refspec '%s' must name a ref",
                                    rs->src);
index df75cb9d4a21a76df3451f925d674b475dc26753..17aabaa679d599090ef5eb01d0c3650108c4f79c 100644 (file)
@@ -12,9 +12,8 @@ static const char * const git_symbolic_ref_usage[] = {
 
 static int check_symref(const char *HEAD, int quiet, int shorten, int print)
 {
-       unsigned char sha1[20];
        int flag;
-       const char *refname = resolve_ref_unsafe(HEAD, 0, sha1, &flag);
+       const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag);
 
        if (!refname)
                die("No such ref: %s", HEAD);
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..7b9307aa588a70a47de4c53a380e165f90924ab9 100644 (file)
@@ -38,7 +38,9 @@ static int prune_worktree(const char *id, struct strbuf *reason)
 {
        struct stat st;
        char *path;
-       int fd, len;
+       int fd;
+       size_t len;
+       ssize_t read_result;
 
        if (!is_directory(git_path("worktrees/%s", id))) {
                strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
@@ -56,10 +58,26 @@ static int prune_worktree(const char *id, struct strbuf *reason)
                            id, strerror(errno));
                return 1;
        }
-       len = st.st_size;
+       len = xsize_t(st.st_size);
        path = xmallocz(len);
-       read_in_full(fd, path, len);
+
+       read_result = read_in_full(fd, path, len);
+       if (read_result < 0) {
+               strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
+                           id, strerror(errno));
+               close(fd);
+               free(path);
+               return 1;
+       }
        close(fd);
+
+       if (read_result != len) {
+               strbuf_addf(reason,
+                           _("Removing worktrees/%s: short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
+                           id, (uintmax_t)len, (uintmax_t)read_result);
+               free(path);
+               return 1;
+       }
        while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
                len--;
        if (!len) {
@@ -381,6 +399,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 9a1f6c49aba4ddd11284723fe781d7fbaa419f95..3310fd210a151545076169b45f5555b52acbbf9d 100644 (file)
@@ -115,7 +115,10 @@ static int stream_to_pack(struct bulk_checkin_state *state,
 
                if (size && !s.avail_in) {
                        ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
-                       if (read_in_full(fd, ibuf, rsize) != rsize)
+                       ssize_t read_result = read_in_full(fd, ibuf, rsize);
+                       if (read_result < 0)
+                               die_errno("failed to read from '%s'", path);
+                       if (read_result != rsize)
                                die("failed to read %d bytes from '%s'",
                                    (int)rsize, path);
                        offset += rsize;
index d15db03c84556af8a47783f4d4ee76379d721020..c092d5d68f32652c229fe232af72d02111f51de7 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -157,9 +157,14 @@ int verify_bundle(struct bundle_header *header, int verbose)
        req_nr = revs.pending.nr;
        setup_revisions(2, argv, &revs, NULL);
 
+       /* Save pending objects, so they can be cleaned up later. */
        refs = revs.pending;
        revs.leak_pending = 1;
 
+       /*
+        * prepare_revision_walk (together with .leak_pending = 1) makes us
+        * the sole owner of the list of pending objects.
+        */
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
 
@@ -176,8 +181,10 @@ int verify_bundle(struct bundle_header *header, int verbose)
                                refs.objects[i].name);
                }
 
+       /* Clean up objects used, as they will be reused. */
        clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
-       free(refs.objects);
+
+       object_array_clear(&refs);
 
        if (verbose) {
                struct ref_list *r;
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..6440e2bf21f5800db98f82fcedff46478188674e 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"
@@ -443,6 +444,7 @@ static inline enum object_type object_type(unsigned int mode)
 #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
 #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
 #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
+#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
 
 /*
  * This environment variable is expected to contain a boolean indicating
@@ -782,6 +784,11 @@ extern int protect_ntfs;
  */
 extern int ref_paranoia;
 
+/*
+ * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
+ */
+int use_optional_locks(void);
+
 /*
  * The character that begins a commented line in user-editable file
  * that is subject to stripspace.
@@ -1243,8 +1250,8 @@ static inline unsigned int hexval(unsigned char c)
  */
 static inline int hex2chr(const char *s)
 {
-       int val = hexval(s[0]);
-       return (val < 0) ? val : (val << 4) | hexval(s[1]);
+       unsigned int val = hexval(s[0]);
+       return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
 }
 
 /* Convert to/from hex/sha1 representation */
@@ -1589,8 +1596,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 333d81e370b188aae2110860df9eaff7b9d9582f..dcaab8ca0437b3adefb523cad531ac1f4b9264e0 100644 (file)
@@ -78,7 +78,7 @@ static MAYBE_UNUSED void init_ ##slabname(struct slabname *s)         \
                                                                        \
 static MAYBE_UNUSED void clear_ ##slabname(struct slabname *s)         \
 {                                                                      \
-       int i;                                                          \
+       unsigned int i;                                                 \
        for (i = 0; i < s->slab_count; i++)                             \
                free(s->slab[i]);                                       \
        s->slab_count = 0;                                              \
@@ -89,13 +89,13 @@ static MAYBE_UNUSED elemtype *slabname## _at_peek(struct slabname *s,       \
                                                  const struct commit *c, \
                                                  int add_if_missing)   \
 {                                                                      \
-       int nth_slab, nth_slot;                                         \
+       unsigned int nth_slab, nth_slot;                                \
                                                                        \
        nth_slab = c->index / s->slab_size;                             \
        nth_slot = c->index % s->slab_size;                             \
                                                                        \
        if (s->slab_count <= nth_slab) {                                \
-               int i;                                                  \
+               unsigned int i;                                         \
                if (!add_if_missing)                                    \
                        return NULL;                                    \
                REALLOC_ARRAY(s->slab, nth_slab + 1);                   \
index 8b28415939035e2b40153013e6f8805422094d84..1e0e633790bb834ad05ed9619e4afa0bac33ce19 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -134,35 +134,41 @@ int register_commit_graft(struct commit_graft *graft, int ignore_dups)
        return 0;
 }
 
-struct commit_graft *read_graft_line(char *buf, int len)
+struct commit_graft *read_graft_line(struct strbuf *line)
 {
        /* The format is just "Commit Parent1 Parent2 ...\n" */
-       int i;
+       int i, phase;
+       const char *tail = NULL;
        struct commit_graft *graft = NULL;
-       const int entry_size = GIT_SHA1_HEXSZ + 1;
+       struct object_id dummy_oid, *oid;
 
-       while (len && isspace(buf[len-1]))
-               buf[--len] = '\0';
-       if (buf[0] == '#' || buf[0] == '\0')
+       strbuf_rtrim(line);
+       if (!line->len || line->buf[0] == '#')
                return NULL;
-       if ((len + 1) % entry_size)
-               goto bad_graft_data;
-       i = (len + 1) / entry_size - 1;
-       graft = xmalloc(st_add(sizeof(*graft), st_mult(GIT_SHA1_RAWSZ, i)));
-       graft->nr_parent = i;
-       if (get_oid_hex(buf, &graft->oid))
-               goto bad_graft_data;
-       for (i = GIT_SHA1_HEXSZ; i < len; i += entry_size) {
-               if (buf[i] != ' ')
-                       goto bad_graft_data;
-               if (get_sha1_hex(buf + i + 1, graft->parent[i/entry_size].hash))
+       /*
+        * phase 0 verifies line, counts hashes in line and allocates graft
+        * phase 1 fills graft
+        */
+       for (phase = 0; phase < 2; phase++) {
+               oid = graft ? &graft->oid : &dummy_oid;
+               if (parse_oid_hex(line->buf, oid, &tail))
                        goto bad_graft_data;
+               for (i = 0; *tail != '\0'; i++) {
+                       oid = graft ? &graft->parent[i] : &dummy_oid;
+                       if (!isspace(*tail++) || parse_oid_hex(tail, oid, &tail))
+                               goto bad_graft_data;
+               }
+               if (!graft) {
+                       graft = xmalloc(st_add(sizeof(*graft),
+                                              st_mult(sizeof(struct object_id), i)));
+                       graft->nr_parent = i;
+               }
        }
        return graft;
 
 bad_graft_data:
-       error("bad graft data: %s", buf);
-       free(graft);
+       error("bad graft data: %s", line->buf);
+       assert(!graft);
        return NULL;
 }
 
@@ -174,7 +180,7 @@ static int read_graft_file(const char *graft_file)
                return -1;
        while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
-               struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
+               struct commit_graft *graft = read_graft_line(&buf);
                if (!graft)
                        continue;
                if (register_commit_graft(graft, 1))
@@ -1080,6 +1086,7 @@ struct commit_list *reduce_heads(struct commit_list *heads)
        num_head = remove_redundant(array, num_head);
        for (i = 0; i < num_head; i++)
                tail = &commit_list_insert(array[i], tail)->next;
+       free(array);
        return result;
 }
 
@@ -1564,10 +1571,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 1283d2a51fdab6d63c75bcdf62adfef3bd2564ba..6d769590f285e534d20bfd8108e8e980253f8815 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -247,7 +247,7 @@ struct commit_graft {
 };
 typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
 
-struct commit_graft *read_graft_line(char *buf, int len);
+struct commit_graft *read_graft_line(struct strbuf *line);
 int register_commit_graft(struct commit_graft *, int);
 struct commit_graft *lookup_commit_graft(const struct object_id *oid);
 
diff --git a/compat/win32/lazyload.h b/compat/win32/lazyload.h
new file mode 100644 (file)
index 0000000..9e631c8
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef LAZYLOAD_H
+#define LAZYLOAD_H
+
+/*
+ * A pair of macros to simplify loading of DLL functions. Example:
+ *
+ *   DECLARE_PROC_ADDR(kernel32.dll, BOOL, CreateHardLinkW,
+ *                     LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
+ *
+ *   if (!INIT_PROC_ADDR(CreateHardLinkW))
+ *           return error("Could not find CreateHardLinkW() function";
+ *
+ *   if (!CreateHardLinkW(source, target, NULL))
+ *           return error("could not create hardlink from %S to %S",
+ *                        source, target);
+ */
+
+struct proc_addr {
+       const char *const dll;
+       const char *const function;
+       FARPROC pfunction;
+       unsigned initialized : 1;
+};
+
+/* Declares a function to be loaded dynamically from a DLL. */
+#define DECLARE_PROC_ADDR(dll, rettype, function, ...) \
+       static struct proc_addr proc_addr_##function = \
+       { #dll, #function, NULL, 0 }; \
+       static rettype (WINAPI *function)(__VA_ARGS__)
+
+/*
+ * Loads a function from a DLL (once-only).
+ * Returns non-NULL function pointer on success.
+ * Returns NULL + errno == ENOSYS on failure.
+ * This function is not thread-safe.
+ */
+#define INIT_PROC_ADDR(function) \
+       (function = get_proc_addr(&proc_addr_##function))
+
+static inline void *get_proc_addr(struct proc_addr *proc)
+{
+       /* only do this once */
+       if (!proc->initialized) {
+               HANDLE hnd;
+               proc->initialized = 1;
+               hnd = LoadLibraryExA(proc->dll, NULL,
+                                    LOAD_LIBRARY_SEARCH_SYSTEM32);
+               if (hnd)
+                       proc->pfunction = GetProcAddress(hnd, proc->function);
+       }
+       /* set ENOSYS if DLL or function was not found */
+       if (!proc->pfunction)
+               errno = ENOSYS;
+       return proc->pfunction;
+}
+
+#endif
index d0d8ce823ae6d8be4b9cc92e1d01e44ca7569e84..4831c1273542706437139ccc379afb38e7ca3d3b 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);
@@ -2205,7 +2200,7 @@ static struct {
        size_t *offset;
        unsigned int offset_alloc;
        enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
-       int seen;
+       unsigned int seen;
 } store;
 
 static int matches(const char *key, const char *value)
@@ -2297,10 +2292,10 @@ static int write_error(const char *filename)
        return 4;
 }
 
-static int store_write_section(int fd, const char *key)
+static struct strbuf store_create_section(const char *key)
 {
        const char *dot;
-       int i, success;
+       int i;
        struct strbuf sb = STRBUF_INIT;
 
        dot = memchr(key, '.', store.baselen);
@@ -2316,15 +2311,24 @@ 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;
+       return sb;
+}
+
+static ssize_t write_section(int fd, const char *key)
+{
+       struct strbuf sb = store_create_section(key);
+       ssize_t ret;
+
+       ret = write_in_full(fd, sb.buf, sb.len) == 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;
@@ -2358,16 +2362,17 @@ static int store_write_pair(int fd, const char *key, const char *value)
                case '"':
                case '\\':
                        strbuf_addch(&sb, '\\');
+                       /* fallthrough */
                default:
                        strbuf_addch(&sb, value[i]);
                        break;
                }
        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 +2455,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 +2474,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 +2501,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 +2587,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 +2612,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 +2624,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 +2662,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;
 
 }
@@ -2757,8 +2750,8 @@ static int section_name_is_ok(const char *name)
 }
 
 /* if new_name == NULL, the section is removed instead */
-int git_config_rename_section_in_file(const char *config_filename,
-                                     const char *old_name, const char *new_name)
+static int git_config_copy_or_rename_section_in_file(const char *config_filename,
+                                     const char *old_name, const char *new_name, int copy)
 {
        int ret = 0, remove = 0;
        char *filename_buf = NULL;
@@ -2767,6 +2760,7 @@ int git_config_rename_section_in_file(const char *config_filename,
        char buf[1024];
        FILE *config_file = NULL;
        struct stat st;
+       struct strbuf copystr = STRBUF_INIT;
 
        if (new_name && !section_name_is_ok(new_name)) {
                ret = error("invalid section name: %s", new_name);
@@ -2805,12 +2799,30 @@ int git_config_rename_section_in_file(const char *config_filename,
        while (fgets(buf, sizeof(buf), config_file)) {
                int i;
                int length;
+               int is_section = 0;
                char *output = buf;
                for (i = 0; buf[i] && isspace(buf[i]); i++)
                        ; /* do nothing */
                if (buf[i] == '[') {
                        /* it's a section */
-                       int offset = section_name_match(&buf[i], old_name);
+                       int offset;
+                       is_section = 1;
+
+                       /*
+                        * When encountering a new section under -c we
+                        * need to flush out any section we're already
+                        * coping and begin anew. There might be
+                        * multiple [branch "$name"] sections.
+                        */
+                       if (copystr.len > 0) {
+                               if (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {
+                                       ret = write_error(get_lock_file_path(lock));
+                                       goto out;
+                               }
+                               strbuf_reset(&copystr);
+                       }
+
+                       offset = section_name_match(&buf[i], old_name);
                        if (offset > 0) {
                                ret++;
                                if (new_name == NULL) {
@@ -2818,25 +2830,29 @@ 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)) {
-                                       ret = write_error(get_lock_file_path(lock));
-                                       goto out;
-                               }
-                               /*
-                                * We wrote out the new section, with
-                                * a newline, now skip the old
-                                * section's length
-                                */
-                               output += offset + i;
-                               if (strlen(output) > 0) {
+                               if (!copy) {
+                                       if (write_section(out_fd, new_name) < 0) {
+                                               ret = write_error(get_lock_file_path(lock));
+                                               goto out;
+                                       }
                                        /*
-                                        * More content means there's
-                                        * a declaration to put on the
-                                        * next line; indent with a
-                                        * tab
+                                        * We wrote out the new section, with
+                                        * a newline, now skip the old
+                                        * section's length
                                         */
-                                       output -= 1;
-                                       output[0] = '\t';
+                                       output += offset + i;
+                                       if (strlen(output) > 0) {
+                                               /*
+                                                * More content means there's
+                                                * a declaration to put on the
+                                                * next line; indent with a
+                                                * tab
+                                                */
+                                               output -= 1;
+                                               output[0] = '\t';
+                                       }
+                               } else {
+                                       copystr = store_create_section(new_name);
                                }
                        }
                        remove = 0;
@@ -2844,11 +2860,30 @@ 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 (!is_section && copystr.len > 0) {
+                       strbuf_add(&copystr, output, length);
+               }
+
+               if (write_in_full(out_fd, output, length) < 0) {
+                       ret = write_error(get_lock_file_path(lock));
+                       goto out;
+               }
+       }
+
+       /*
+        * Copy a trailing section at the end of the config, won't be
+        * flushed by the usual "flush because we have a new section
+        * logic in the loop above.
+        */
+       if (copystr.len > 0) {
+               if (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {
                        ret = write_error(get_lock_file_path(lock));
                        goto out;
                }
+               strbuf_reset(&copystr);
        }
+
        fclose(config_file);
        config_file = NULL;
 commit_and_out:
@@ -2864,11 +2899,30 @@ int git_config_rename_section_in_file(const char *config_filename,
        return ret;
 }
 
+int git_config_rename_section_in_file(const char *config_filename,
+                                     const char *old_name, const char *new_name)
+{
+       return git_config_copy_or_rename_section_in_file(config_filename,
+                                        old_name, new_name, 0);
+}
+
 int git_config_rename_section(const char *old_name, const char *new_name)
 {
        return git_config_rename_section_in_file(NULL, old_name, new_name);
 }
 
+int git_config_copy_section_in_file(const char *config_filename,
+                                     const char *old_name, const char *new_name)
+{
+       return git_config_copy_or_rename_section_in_file(config_filename,
+                                        old_name, new_name, 1);
+}
+
+int git_config_copy_section(const char *old_name, const char *new_name)
+{
+       return git_config_copy_section_in_file(NULL, old_name, new_name);
+}
+
 /*
  * Call this to report error for your variable that should not
  * get a boolean value (i.e. "[my] var" means "true").
index 97471b887320b48f1b4c2d5a2023f6ffd1acf94e..a49d26441622508fd3c6560ac9784e84541c10dc 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 *);
@@ -71,6 +70,8 @@ extern int git_config_set_multivar_in_file_gently(const char *, const char *, co
 extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
 extern int git_config_rename_section(const char *, const char *);
 extern int git_config_rename_section_in_file(const char *, const char *, const char *);
+extern int git_config_copy_section(const char *, const char *);
+extern int git_config_copy_section_in_file(const char *, const char *, const char *);
 extern const char *git_etc_gitconfig(void);
 extern int git_env_bool(const char *, int);
 extern unsigned long git_env_ulong(const char *, unsigned long);
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..20d7ab67bdf889feb255e01a9b18cf4efa6107a3 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)
@@ -1542,8 +1545,9 @@ static int ident_filter_fn(struct stream_filter *filter,
                switch (ident->state) {
                default:
                        strbuf_add(&ident->left, head, ident->state);
+                       /* fallthrough */
                case IDENT_SKIPPING:
-                       /* fallthru */
+                       /* fallthrough */
                case IDENT_DRAINING:
                        ident_drain(ident, &output, osize_p);
                }
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);
index a172199e44bf370cd66ee7073f5a2228fd9679ef..2adae04073816a781d01d85433d8d8922baafd7f 100644 (file)
@@ -19,7 +19,7 @@ static void flush(struct sha1file *f, const void *buf, unsigned int count)
 
                if (ret < 0)
                        die_errno("%s: sha1 file read error", f->name);
-               if (ret < count)
+               if (ret != count)
                        die("%s: sha1 file truncated", f->name);
                if (memcmp(buf, check_buffer, count))
                        die("sha1 file '%s' validation error", f->name);
index 3797ce6041981d1facda44d3a95d4448dbb8a091..e49643353bf56807b3d4ac4011784b5d8dd6f7a4 100644 (file)
@@ -319,7 +319,9 @@ create_delta(const struct delta_index *index,
             const void *trg_buf, unsigned long trg_size,
             unsigned long *delta_size, unsigned long max_size)
 {
-       unsigned int i, outpos, outsize, moff, msize, val;
+       unsigned int i, val;
+       off_t outpos, moff;
+       size_t l, outsize, msize;
        int inscnt;
        const unsigned char *ref_data, *ref_top, *data, *top;
        unsigned char *out;
@@ -336,20 +338,20 @@ create_delta(const struct delta_index *index,
                return NULL;
 
        /* store reference buffer size */
-       i = index->src_size;
-       while (i >= 0x80) {
-               out[outpos++] = i | 0x80;
-               i >>= 7;
+       l = index->src_size;
+       while (l >= 0x80) {
+               out[outpos++] = l | 0x80;
+               l >>= 7;
        }
-       out[outpos++] = i;
+       out[outpos++] = l;
 
        /* store target buffer size */
-       i = trg_size;
-       while (i >= 0x80) {
-               out[outpos++] = i | 0x80;
-               i >>= 7;
+       l = trg_size;
+       while (l >= 0x80) {
+               out[outpos++] = l | 0x80;
+               l >>= 7;
        }
-       out[outpos++] = i;
+       out[outpos++] = l;
 
        ref_data = index->src_buf;
        ref_top = ref_data + index->src_size;
@@ -452,6 +454,9 @@ create_delta(const struct delta_index *index,
                        moff += msize;
                        msize = left;
 
+                       if (moff > 0xffffffff)
+                               msize = 0;
+
                        if (msize < 4096) {
                                int j;
                                val = 0;
index 2a52b079546cb01d8dfe708f7e7c7e38ec582435..4e0980caa80fb980938f68c8d350ec00cc24398b 100644 (file)
@@ -549,7 +549,6 @@ int index_differs_from(const char *def, int diff_flags,
        rev.diffopt.flags |= diff_flags;
        rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
        run_diff_index(&rev, 1);
-       if (rev.pending.alloc)
-               free(rev.pending.objects);
+       object_array_clear(&rev.pending);
        return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0);
 }
diff --git a/diff.c b/diff.c
index 3d3e553a98bc4fd83d2f79515056c4116086b5df..69f03570adf6cf2c3642aea1fb0dba5011ef966f 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;
        }
@@ -1541,7 +1541,7 @@ static void emit_rewrite_diff(const char *name_a,
 
 struct diff_words_buffer {
        mmfile_t text;
-       long alloc;
+       unsigned long alloc;
        struct diff_words_orig {
                const char *begin, *end;
        } *orig;
@@ -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);
@@ -5272,6 +5272,7 @@ static void show_mode_change(struct diff_options *opt, struct diff_filepair *p,
                        strbuf_addch(&sb, ' ');
                        quote_c_style(p->two->path, &sb, NULL, 0);
                }
+               strbuf_addch(&sb, '\n');
                emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
                                 sb.buf, sb.len, 0);
                strbuf_release(&sb);
@@ -5289,6 +5290,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 +5316,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..8289c25b44d74a0f054f9fe3eaf44c5fdcf0b3d2 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)
@@ -336,3 +338,8 @@ void reset_shared_repository(void)
 {
        need_shared_repository_from_config = 1;
 }
+
+int use_optional_locks(void)
+{
+       return git_env_bool(GIT_OPTIONAL_LOCKS_ENVIRONMENT, 1);
+}
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");
 }
 
diff --git a/fsck.c b/fsck.c
index 2d2d2e9432b96e5ff2ad893bd8661c0157d42eee..2ad00fc4d0356d8ec2cac21d1897a76b5002fd5b 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -588,6 +588,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
                case S_IFREG | 0664:
                        if (!options->strict)
                                break;
+                       /* fallthrough */
                default:
                        has_bad_modes = 1;
                }
index 9cb123a07df88c975cf1b0d6c5832cb410175dd0..b7c173c345544d61a887cfbebb58703478a29d2d 100755 (executable)
@@ -983,7 +983,7 @@ sub find_parents {
        # check that we actually know about the branch
        next unless -e "$git_dir/refs/heads/$branch";
 
-       my $mergebase = `git-merge-base $branch $ps->{branch}`;
+       my $mergebase = safe_pipe_capture(qw(git-merge-base), $branch, $ps->{branch});
        if ($?) {
            # Don't die here, Arch supports one-way cherry-picking
            # between branches with no common base (or any relationship
@@ -1074,7 +1074,7 @@ sub find_parents {
 
 sub git_rev_parse {
     my $name = shift;
-    my $val  = `git-rev-parse $name`;
+    my $val  = safe_pipe_capture(qw(git-rev-parse), $name);
     die "Error: git-rev-parse $name" if $?;
     chomp $val;
     return $val;
index 6678b488cc73851d468094deb5352d311ab310d0..cedad4d5818a6e5c6e1732d34cf8fef6dab36718 100644 (file)
@@ -898,9 +898,11 @@ static inline char *xstrdup_or_null(const char *str)
 
 static inline size_t xsize_t(off_t len)
 {
-       if (len > (size_t) len)
+       size_t size = (size_t) len;
+
+       if (len != (off_t) size)
                die("Cannot handle files this big");
-       return (size_t)len;
+       return size;
 }
 
 __attribute__((format (printf, 3, 4)))
@@ -1169,4 +1171,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 1e4e65a45d16cfcc5a36e6855216e9be8fe0e0b4..36929921ea79006dc659f6996ae158131ccf2b73 100755 (executable)
@@ -642,6 +642,7 @@ sub is_sha1 {
 
 sub get_headref ($) {
        my $name = shift;
+       $name =~ s/'/'\\''/;
        my $r = `git rev-parse --verify '$name' 2>/dev/null`;
        return undef unless $? == 0;
        chomp $r;
index d50c85ed7ba719a0dc04f7ca96cf05216104599d..ae1044273d01125af96a06f1d68cbbd3d0e25c41 100755 (executable)
@@ -356,7 +356,7 @@ sub req_Root
        return 0;
     }
 
-    my @gitvars = `git config -l`;
+    my @gitvars = safe_pipe_capture(qw(git config -l));
     if ($?) {
        print "E problems executing git-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
         print "E \n";
@@ -841,7 +841,7 @@ sub req_Modified
     # Save the file data in $state
     $state->{entries}{$state->{directory}.$data}{modified_filename} = $filename;
     $state->{entries}{$state->{directory}.$data}{modified_mode} = $mode;
-    $state->{entries}{$state->{directory}.$data}{modified_hash} = `git hash-object $filename`;
+    $state->{entries}{$state->{directory}.$data}{modified_hash} = safe_pipe_capture('git','hash-object',$filename);
     $state->{entries}{$state->{directory}.$data}{modified_hash} =~ s/\s.*$//s;
 
     #$log->debug("req_Modified : file=$data mode=$mode size=$size");
@@ -943,7 +943,7 @@ sub req_co
 
     # Provide list of modules, if -c was used.
     if (exists $state->{opt}{c}) {
-        my $showref = `git show-ref --heads`;
+        my $showref = safe_pipe_capture(qw(git show-ref --heads));
         for my $line (split '\n', $showref) {
             if ( $line =~ m% refs/heads/(.*)$% ) {
                 print "M $1\t$1\n";
@@ -1181,7 +1181,7 @@ sub req_update
     # projects (heads in this case) to checkout.
     #
     if ($state->{module} eq '') {
-        my $showref = `git show-ref --heads`;
+        my $showref = safe_pipe_capture(qw(git show-ref --heads));
         print "E cvs update: Updating .\n";
         for my $line (split '\n', $showref) {
             if ( $line =~ m% refs/heads/(.*)$% ) {
@@ -1463,7 +1463,7 @@ sub req_update
                 # transmit file, format is single integer on a line by itself (file
                 # size) followed by the file contents
                 # TODO : we should copy files in blocks
-                my $data = `cat $mergedFile`;
+                my $data = safe_pipe_capture('cat', $mergedFile);
                 $log->debug("File size : " . length($data));
                 print length($data) . "\n";
                 print $data;
@@ -1579,7 +1579,7 @@ sub req_ci
                 $branchRef = "refs/heads/$stickyInfo->{tag}";
             }
 
-            $parenthash = `git show-ref -s $branchRef`;
+            $parenthash = safe_pipe_capture('git', 'show-ref', '-s', $branchRef);
             chomp $parenthash;
             if ($parenthash !~ /^[0-9a-f]{40}$/)
             {
@@ -1687,7 +1687,7 @@ sub req_ci
         return;
     }
 
-    my $treehash = `git write-tree`;
+    my $treehash = safe_pipe_capture(qw(git write-tree));
     chomp $treehash;
 
     $log->debug("Treehash : $treehash, Parenthash : $parenthash");
@@ -1704,7 +1704,7 @@ sub req_ci
     }
     close $msg_fh;
 
-    my $commithash = `git commit-tree $treehash -p $parenthash < $msg_filename`;
+    my $commithash = safe_pipe_capture('git', 'commit-tree', $treehash, '-p', $parenthash, '-F', $msg_filename);
     chomp($commithash);
     $log->info("Commit hash : $commithash");
 
@@ -2854,12 +2854,12 @@ sub transmitfile
 
     die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{40}$/ );
 
-    my $type = `git cat-file -t $filehash`;
+    my $type = safe_pipe_capture('git', 'cat-file', '-t', $filehash);
     chomp $type;
 
     die ( "Invalid type '$type' (expected 'blob')" ) unless ( defined ( $type ) and $type eq "blob" );
 
-    my $size = `git cat-file -s $filehash`;
+    my $size = safe_pipe_capture('git', 'cat-file', '-s', $filehash);
     chomp $size;
 
     $log->debug("transmitfile($filehash) size=$size, type=$type");
@@ -3040,7 +3040,7 @@ sub ensureWorkTree
     chdir $work->{emptyDir} or
         die "Unable to chdir to $work->{emptyDir}\n";
 
-    my $ver = `git show-ref -s refs/heads/$state->{module}`;
+    my $ver = safe_pipe_capture('git', 'show-ref', '-s', "refs/heads/$state->{module}");
     chomp $ver;
     if ($ver !~ /^[0-9a-f]{40}$/)
     {
@@ -3287,7 +3287,7 @@ sub open_blob_or_die
             die "Need filehash\n";
         }
 
-        my $type = `git cat-file -t $name`;
+        my $type = safe_pipe_capture('git', 'cat-file', '-t', $name);
         chomp $type;
 
         unless ( defined ( $type ) and $type eq "blob" )
@@ -3296,7 +3296,7 @@ sub open_blob_or_die
             die ( "Invalid type '$type' (expected 'blob')" )
         }
 
-        my $size = `git cat-file -s $name`;
+        my $size = safe_pipe_capture('git', 'cat-file', '-s', $name);
         chomp $size;
 
         $log->debug("open_blob_or_die($name) size=$size, type=$type");
@@ -3406,6 +3406,22 @@ sub refHashEqual
     return $out;
 }
 
+# an alternative to `command` that allows input to be passed as an array
+# to work around shell problems with weird characters in arguments
+
+sub safe_pipe_capture {
+
+    my @output;
+
+    if (my $pid = open my $child, '-|') {
+        @output = (<$child>);
+        close $child or die join(' ',@_).": $! $?";
+    } else {
+        exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
+    }
+    return wantarray ? @output : join('',@output);
+}
+
 
 package GITCVS::log;
 
@@ -3797,10 +3813,10 @@ sub update
     # first lets get the commit list
     $ENV{GIT_DIR} = $self->{git_path};
 
-    my $commitsha1 = `git rev-parse $self->{module}`;
+    my $commitsha1 = ::safe_pipe_capture('git', 'rev-parse', $self->{module});
     chomp $commitsha1;
 
-    my $commitinfo = `git cat-file commit $self->{module} 2>&1`;
+    my $commitinfo = ::safe_pipe_capture('git', 'cat-file', 'commit', $self->{module});
     unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
     {
         die("Invalid module '$self->{module}'");
@@ -3882,7 +3898,7 @@ sub update
                     # several candidate merge bases. let's assume
                     # that the first one is the best one.
                    my $base = eval {
-                           safe_pipe_capture('git', 'merge-base',
+                           ::safe_pipe_capture('git', 'merge-base',
                                                 $lastpicked, $parent);
                    };
                    # The two branches may not be related at all,
@@ -4749,7 +4765,7 @@ sub getMetaFromCommithash
         return $retVal;
     }
 
-    my($fileHash)=safe_pipe_capture("git","rev-parse","$revCommit:$filename");
+    my($fileHash) = ::safe_pipe_capture("git","rev-parse","$revCommit:$filename");
     chomp $fileHash;
     if(!($fileHash=~/^[0-9a-f]{40}$/))
     {
@@ -4844,8 +4860,8 @@ sub lookupCommitRef
         return $commitHash;
     }
 
-    $commitHash=safe_pipe_capture("git","rev-parse","--verify","--quiet",
-                                  $self->unescapeRefName($ref));
+    $commitHash = ::safe_pipe_capture("git","rev-parse","--verify","--quiet",
+                                     $self->unescapeRefName($ref));
     $commitHash=~s/\s*$//;
     if(!($commitHash=~/^[0-9a-f]{40}$/))
     {
@@ -4854,7 +4870,7 @@ sub lookupCommitRef
 
     if( defined($commitHash) )
     {
-        my $type=safe_pipe_capture("git","cat-file","-t",$commitHash);
+        my $type = ::safe_pipe_capture("git","cat-file","-t",$commitHash);
         if( ! ($type=~/^commit\s*$/ ) )
         {
             $commitHash=undef;
@@ -4907,7 +4923,7 @@ sub commitmessage
         return $message;
     }
 
-    my @lines = safe_pipe_capture("git", "cat-file", "commit", $commithash);
+    my @lines = ::safe_pipe_capture("git", "cat-file", "commit", $commithash);
     shift @lines while ( $lines[0] =~ /\S/ );
     $message = join("",@lines);
     $message .= " " if ( $message =~ /\n$/ );
@@ -5056,25 +5072,6 @@ sub in_array
     return $retval;
 }
 
-=head2 safe_pipe_capture
-
-an alternative to `command` that allows input to be passed as an array
-to work around shell problems with weird characters in arguments
-
-=cut
-sub safe_pipe_capture {
-
-    my @output;
-
-    if (my $pid = open my $child, '-|') {
-        @output = (<$child>);
-        close $child or die join(' ',@_).": $! $?";
-    } else {
-        exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
-    }
-    return wantarray ? @output : join('',@output);
-}
-
 =head2 mangle_dirname
 
 create a string from a directory name that is suitable to use as
index 3a74602ef37712e16dc28567130ca8ee9e8fc579..3365a3b866b510db5bf9ca20a0341c34c21b7a69 100755 (executable)
@@ -86,7 +86,7 @@ USAGE="[--setup <command>] [--env-filter <command>]
        [--parent-filter <command>] [--msg-filter <command>]
        [--commit-filter <command>] [--tag-name-filter <command>]
        [--subdirectory-filter <directory>] [--original <namespace>]
-       [-d <directory>] [-f | --force]
+       [-d <directory>] [-f | --force] [--state-branch <branch>]
        [--] [<rev-list options>...]"
 
 OPTIONS_SPEC=
@@ -106,6 +106,7 @@ filter_msg=cat
 filter_commit=
 filter_tag_name=
 filter_subdir=
+state_branch=
 orig_namespace=refs/original/
 force=
 prune_empty=
@@ -181,6 +182,9 @@ do
        --original)
                orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
                ;;
+       --state-branch)
+               state_branch="$OPTARG"
+               ;;
        *)
                usage
                ;;
@@ -219,6 +223,13 @@ trap 'cd "$orig_dir"; rm -rf "$tempdir"' 0
 ORIG_GIT_DIR="$GIT_DIR"
 ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
 ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
+ORIG_GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME"
+ORIG_GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL"
+ORIG_GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE"
+ORIG_GIT_COMMITTER_NAME="$GIT_COMMITTER_NAME"
+ORIG_GIT_COMMITTER_EMAIL="$GIT_COMMITTER_EMAIL"
+ORIG_GIT_COMMITTER_DATE="$GIT_COMMITTER_DATE"
+
 GIT_WORK_TREE=.
 export GIT_DIR GIT_WORK_TREE
 
@@ -252,6 +263,26 @@ export GIT_INDEX_FILE
 # map old->new commit ids for rewriting parents
 mkdir ../map || die "Could not create map/ directory"
 
+if test -n "$state_branch"
+then
+       state_commit=$(git rev-parse --no-flags --revs-only "$state_branch")
+       if test -n "$state_commit"
+       then
+               echo "Populating map from $state_branch ($state_commit)" 1>&2
+               perl -e'open(MAP, "-|", "git show $ARGV[0]:filter.map") or die;
+                       while (<MAP>) {
+                               m/(.*):(.*)/ or die;
+                               open F, ">../map/$1" or die;
+                               print F "$2" or die;
+                               close(F) or die;
+                       }
+                       close(MAP) or die;' "$state_commit" \
+                               || die "Unable to load state from $state_branch:filter.map"
+       else
+               echo "Branch $state_branch does not exist. Will create" 1>&2
+       fi
+fi
+
 # we need "--" only if there are no path arguments in $@
 nonrevs=$(git rev-parse --no-revs "$@") || exit
 if test -z "$nonrevs"
@@ -530,7 +561,7 @@ if [ "$filter_tag_name" ]; then
                                        }' \
                                    -e '/^-----BEGIN PGP SIGNATURE-----/q' \
                                    -e 'p' ) |
-                               git mktag) ||
+                               git hash-object -t tag -w --stdin) ||
                                die "Could not create new tag object for $ref"
                        if git cat-file tag "$ref" | \
                           sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
@@ -544,12 +575,9 @@ if [ "$filter_tag_name" ]; then
        done
 fi
 
-cd "$orig_dir"
-rm -rf "$tempdir"
-
-trap - 0
-
 unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
+unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
+unset GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE
 test -z "$ORIG_GIT_DIR" || {
        GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
 }
@@ -561,6 +589,58 @@ test -z "$ORIG_GIT_INDEX_FILE" || {
        GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
        export GIT_INDEX_FILE
 }
+test -z "$ORIG_GIT_AUTHOR_NAME" || {
+       GIT_AUTHOR_NAME="$ORIG_GIT_AUTHOR_NAME" &&
+       export GIT_AUTHOR_NAME
+}
+test -z "$ORIG_GIT_AUTHOR_EMAIL" || {
+       GIT_AUTHOR_EMAIL="$ORIG_GIT_AUTHOR_EMAIL" &&
+       export GIT_AUTHOR_EMAIL
+}
+test -z "$ORIG_GIT_AUTHOR_DATE" || {
+       GIT_AUTHOR_DATE="$ORIG_GIT_AUTHOR_DATE" &&
+       export GIT_AUTHOR_DATE
+}
+test -z "$ORIG_GIT_COMMITTER_NAME" || {
+       GIT_COMMITTER_NAME="$ORIG_GIT_COMMITTER_NAME" &&
+       export GIT_COMMITTER_NAME
+}
+test -z "$ORIG_GIT_COMMITTER_EMAIL" || {
+       GIT_COMMITTER_EMAIL="$ORIG_GIT_COMMITTER_EMAIL" &&
+       export GIT_COMMITTER_EMAIL
+}
+test -z "$ORIG_GIT_COMMITTER_DATE" || {
+       GIT_COMMITTER_DATE="$ORIG_GIT_COMMITTER_DATE" &&
+       export GIT_COMMITTER_DATE
+}
+
+if test -n "$state_branch"
+then
+       echo "Saving rewrite state to $state_branch" 1>&2
+       state_blob=$(
+               perl -e'opendir D, "../map" or die;
+                       open H, "|-", "git hash-object -w --stdin" or die;
+                       foreach (sort readdir(D)) {
+                               next if m/^\.\.?$/;
+                               open F, "<../map/$_" or die;
+                               chomp($f = <F>);
+                               print H "$_:$f\n" or die;
+                       }
+                       close(H) or die;' || die "Unable to save state")
+       state_tree=$(/bin/echo -e "100644 blob $state_blob\tfilter.map" | git mktree)
+       if test -n "$state_commit"
+       then
+               state_commit=$(/bin/echo "Sync" | git commit-tree "$state_tree" -p "$state_commit")
+       else
+               state_commit=$(/bin/echo "Sync" | git commit-tree "$state_tree" )
+       fi
+       git update-ref "$state_branch" "$state_commit"
+fi
+
+cd "$orig_dir"
+rm -rf "$tempdir"
+
+trap - 0
 
 if [ "$(is_bare_repository)" = false ]; then
        git read-tree -u -m HEAD || exit
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 29b7e8824b53abeaa68780b95d5954f67f734098..2563dc52daaf7acbbdba803360f94bfa5040c5b6 100644 (file)
@@ -155,13 +155,13 @@ reschedule_last_action () {
 append_todo_help () {
        gettext "
 Commands:
- p, pick = use commit
- r, reword = use commit, but edit the commit message
- e, edit = use commit, but stop for amending
- s, squash = use commit, but meld into previous commit
- f, fixup = like \"squash\", but discard this commit's log message
- x, exec = run command (the rest of the line) using shell
- d, drop = remove commit
+p, pick = use commit
+r, reword = use commit, but edit the commit message
+e, edit = use commit, but stop for amending
+s, squash = use commit, but meld into previous commit
+f, fixup = like \"squash\", but discard this commit's log message
+x, exec = run command (the rest of the line) using shell
+d, drop = remove commit
 
 These lines can be re-ordered; they are executed from top to bottom.
 " | git stripspace --comment-lines >>"$todo"
@@ -714,154 +714,12 @@ do_rest () {
        done
 }
 
-# skip picking commits whose parents are unchanged
-skip_unnecessary_picks () {
-       fd=3
-       while read -r command rest
-       do
-               # fd=3 means we skip the command
-               case "$fd,$command" in
-               3,pick|3,p)
-                       # pick a commit whose parent is current $onto -> skip
-                       sha1=${rest%% *}
-                       case "$(git rev-parse --verify --quiet "$sha1"^)" in
-                       "$onto"*)
-                               onto=$sha1
-                               ;;
-                       *)
-                               fd=1
-                               ;;
-                       esac
-                       ;;
-               3,"$comment_char"*|3,)
-                       # copy comments
-                       ;;
-               *)
-                       fd=1
-                       ;;
-               esac
-               printf '%s\n' "$command${rest:+ }$rest" >&$fd
-       done <"$todo" >"$todo.new" 3>>"$done" &&
-       mv -f "$todo".new "$todo" &&
-       case "$(peek_next_command)" in
-       squash|s|fixup|f)
-               record_in_rewritten "$onto"
-               ;;
-       esac ||
-               die "$(gettext "Could not skip unnecessary pick commands")"
-}
-
-transform_todo_ids () {
-       while read -r command rest
-       do
-               case "$command" in
-               "$comment_char"* | exec)
-                       # Be careful for oddball commands like 'exec'
-                       # that do not have a SHA-1 at the beginning of $rest.
-                       ;;
-               *)
-                       sha1=$(git rev-parse --verify --quiet "$@" ${rest%%[     ]*}) &&
-                       rest="$sha1 ${rest#*[    ]}"
-                       ;;
-               esac
-               printf '%s\n' "$command${rest:+ }$rest"
-       done <"$todo" >"$todo.new" &&
-       mv -f "$todo.new" "$todo"
-}
-
 expand_todo_ids() {
-       transform_todo_ids
+       git rebase--helper --expand-ids
 }
 
 collapse_todo_ids() {
-       transform_todo_ids --short
-}
-
-# Rearrange the todo list that has both "pick sha1 msg" and
-# "pick sha1 fixup!/squash! msg" appears in it so that the latter
-# comes immediately after the former, and change "pick" to
-# "fixup"/"squash".
-#
-# Note that if the config has specified a custom instruction format
-# each log message will be re-retrieved in order to normalize the
-# autosquash arrangement
-rearrange_squash () {
-       # extract fixup!/squash! lines and resolve any referenced sha1's
-       while read -r pick sha1 message
-       do
-               test -z "${format}" || message=$(git log -n 1 --format="%s" ${sha1})
-               case "$message" in
-               "squash! "*|"fixup! "*)
-                       action="${message%%!*}"
-                       rest=$message
-                       prefix=
-                       # skip all squash! or fixup! (but save for later)
-                       while :
-                       do
-                               case "$rest" in
-                               "squash! "*|"fixup! "*)
-                                       prefix="$prefix${rest%%!*},"
-                                       rest="${rest#*! }"
-                                       ;;
-                               *)
-                                       break
-                                       ;;
-                               esac
-                       done
-                       printf '%s %s %s %s\n' "$sha1" "$action" "$prefix" "$rest"
-                       # if it's a single word, try to resolve to a full sha1 and
-                       # emit a second copy. This allows us to match on both message
-                       # and on sha1 prefix
-                       if test "${rest#* }" = "$rest"; then
-                               fullsha="$(git rev-parse -q --verify "$rest" 2>/dev/null)"
-                               if test -n "$fullsha"; then
-                                       # prefix the action to uniquely identify this line as
-                                       # intended for full sha1 match
-                                       echo "$sha1 +$action $prefix $fullsha"
-                               fi
-                       fi
-               esac
-       done >"$1.sq" <"$1"
-       test -s "$1.sq" || return
-
-       used=
-       while read -r pick sha1 message
-       do
-               case " $used" in
-               *" $sha1 "*) continue ;;
-               esac
-               printf '%s\n' "$pick $sha1 $message"
-               test -z "${format}" || message=$(git log -n 1 --format="%s" ${sha1})
-               used="$used$sha1 "
-               while read -r squash action msg_prefix msg_content
-               do
-                       case " $used" in
-                       *" $squash "*) continue ;;
-                       esac
-                       emit=0
-                       case "$action" in
-                       +*)
-                               action="${action#+}"
-                               # full sha1 prefix test
-                               case "$msg_content" in "$sha1"*) emit=1;; esac ;;
-                       *)
-                               # message prefix test
-                               case "$message" in "$msg_content"*) emit=1;; esac ;;
-                       esac
-                       if test $emit = 1; then
-                               if test -n "${format}"
-                               then
-                                       msg_content=$(git log -n 1 --format="${format}" ${squash})
-                               else
-                                       msg_content="$(echo "$msg_prefix" | sed "s/,/! /g")$msg_content"
-                               fi
-                               printf '%s\n' "$action $squash $msg_content"
-                               used="$used$squash "
-                       fi
-               done <"$1.sq"
-       done >"$1.rearranged" <"$1"
-       cat "$1.rearranged" >"$1"
-       rm -f "$1.sq" "$1.rearranged"
+       git rebase--helper --shorten-ids
 }
 
 # Add commands after a pick or after a squash/fixup serie
@@ -885,96 +743,6 @@ add_exec_commands () {
        mv "$1.new" "$1"
 }
 
-# Check if the SHA-1 passed as an argument is a
-# correct one, if not then print $2 in "$todo".badsha
-# $1: the SHA-1 to test
-# $2: the line number of the input
-# $3: the input filename
-check_commit_sha () {
-       badsha=0
-       if test -z "$1"
-       then
-               badsha=1
-       else
-               sha1_verif="$(git rev-parse --verify --quiet $1^{commit})"
-               if test -z "$sha1_verif"
-               then
-                       badsha=1
-               fi
-       fi
-
-       if test $badsha -ne 0
-       then
-               line="$(sed -n -e "${2}p" "$3")"
-               warn "$(eval_gettext "\
-Warning: the SHA-1 is missing or isn't a commit in the following line:
- - \$line")"
-               warn
-       fi
-
-       return $badsha
-}
-
-# prints the bad commits and bad commands
-# from the todolist in stdin
-check_bad_cmd_and_sha () {
-       retval=0
-       lineno=0
-       while read -r command rest
-       do
-               lineno=$(( $lineno + 1 ))
-               case $command in
-               "$comment_char"*|''|noop|x|exec)
-                       # Doesn't expect a SHA-1
-                       ;;
-               "$cr")
-                       # Work around CR left by "read" (e.g. with Git for
-                       # Windows' Bash).
-                       ;;
-               pick|p|drop|d|reword|r|edit|e|squash|s|fixup|f)
-                       if ! check_commit_sha "${rest%%[        ]*}" "$lineno" "$1"
-                       then
-                               retval=1
-                       fi
-                       ;;
-               *)
-                       line="$(sed -n -e "${lineno}p" "$1")"
-                       warn "$(eval_gettext "\
-Warning: the command isn't recognized in the following line:
- - \$line")"
-                       warn
-                       retval=1
-                       ;;
-               esac
-       done <"$1"
-       return $retval
-}
-
-# Print the list of the SHA-1 of the commits
-# from stdin to stdout
-todo_list_to_sha_list () {
-       git stripspace --strip-comments |
-       while read -r command sha1 rest
-       do
-               case $command in
-               "$comment_char"*|''|noop|x|"exec")
-                       ;;
-               *)
-                       long_sha=$(git rev-list --no-walk "$sha1" 2>/dev/null)
-                       printf "%s\n" "$long_sha"
-                       ;;
-               esac
-       done
-}
-
-# Use warn for each line in stdin
-warn_lines () {
-       while read -r line
-       do
-               warn " - $line"
-       done
-}
-
 # Switch to the branch in $into and notify it in the reflog
 checkout_onto () {
        GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
@@ -989,74 +757,6 @@ get_missing_commit_check_level () {
        printf '%s' "$check_level" | tr 'A-Z' 'a-z'
 }
 
-# Check if the user dropped some commits by mistake
-# Behaviour determined by rebase.missingCommitsCheck.
-# Check if there is an unrecognized command or a
-# bad SHA-1 in a command.
-check_todo_list () {
-       raise_error=f
-
-       check_level=$(get_missing_commit_check_level)
-
-       case "$check_level" in
-       warn|error)
-               # Get the SHA-1 of the commits
-               todo_list_to_sha_list <"$todo".backup >"$todo".oldsha1
-               todo_list_to_sha_list <"$todo" >"$todo".newsha1
-
-               # Sort the SHA-1 and compare them
-               sort -u "$todo".oldsha1 >"$todo".oldsha1+
-               mv "$todo".oldsha1+ "$todo".oldsha1
-               sort -u "$todo".newsha1 >"$todo".newsha1+
-               mv "$todo".newsha1+ "$todo".newsha1
-               comm -2 -3 "$todo".oldsha1 "$todo".newsha1 >"$todo".miss
-
-               # Warn about missing commits
-               if test -s "$todo".miss
-               then
-                       test "$check_level" = error && raise_error=t
-
-                       warn "$(gettext "\
-Warning: some commits may have been dropped accidentally.
-Dropped commits (newer to older):")"
-
-                       # Make the list user-friendly and display
-                       opt="--no-walk=sorted --format=oneline --abbrev-commit --stdin"
-                       git rev-list $opt <"$todo".miss | warn_lines
-
-                       warn "$(gettext "\
-To avoid this message, use \"drop\" to explicitly remove a commit.
-
-Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
-The possible behaviours are: ignore, warn, error.")"
-                       warn
-               fi
-               ;;
-       ignore)
-               ;;
-       *)
-               warn "$(eval_gettext "Unrecognized setting \$check_level for option rebase.missingCommitsCheck. Ignoring.")"
-               ;;
-       esac
-
-       if ! check_bad_cmd_and_sha "$todo"
-       then
-               raise_error=t
-       fi
-
-       if test $raise_error = t
-       then
-               # Checkout before the first commit of the
-               # rebase: this way git rebase --continue
-               # will work correctly as it expects HEAD to be
-               # placed before the commit of the next action
-               checkout_onto
-
-               warn "$(gettext "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.")"
-               die "$(gettext "Or you can abort the rebase with 'git rebase --abort'.")"
-       fi
-}
-
 # The whole contents of this file is run by dot-sourcing it from
 # inside a shell function.  It used to be that "return"s we see
 # below were not inside any function, and expected to return
@@ -1211,26 +911,27 @@ else
        revisions=$onto...$orig_head
        shortrevisions=$shorthead
 fi
-format=$(git config --get rebase.instructionFormat)
-# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
-git rev-list $merges_option --format="%m%H ${format:-%s}" \
-       --reverse --left-right --topo-order \
-       $revisions ${restrict_revision+^$restrict_revision} | \
-       sed -n "s/^>//p" |
-while read -r sha1 rest
-do
-
-       if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
-       then
-               comment_out="$comment_char "
-       else
-               comment_out=
-       fi
+if test t != "$preserve_merges"
+then
+       git rebase--helper --make-script ${keep_empty:+--keep-empty} \
+               $revisions ${restrict_revision+^$restrict_revision} >"$todo"
+else
+       format=$(git config --get rebase.instructionFormat)
+       # the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
+       git rev-list $merges_option --format="%m%H ${format:-%s}" \
+               --reverse --left-right --topo-order \
+               $revisions ${restrict_revision+^$restrict_revision} | \
+               sed -n "s/^>//p" |
+       while read -r sha1 rest
+       do
+
+               if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
+               then
+                       comment_out="$comment_char "
+               else
+                       comment_out=
+               fi
 
-       if test t != "$preserve_merges"
-       then
-               printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
-       else
                if test -z "$rebase_root"
                then
                        preserve=t
@@ -1249,8 +950,8 @@ do
                        touch "$rewritten"/$sha1
                        printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
                fi
-       fi
-done
+       done
+fi
 
 # Watch for commits that been dropped by --cherry-pick
 if test t = "$preserve_merges"
@@ -1280,7 +981,7 @@ then
 fi
 
 test -s "$todo" || echo noop >> "$todo"
-test -n "$autosquash" && rearrange_squash "$todo"
+test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
 test -n "$cmd" && add_exec_commands "$todo"
 
 todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
@@ -1316,11 +1017,17 @@ git_sequence_editor "$todo" ||
 has_action "$todo" ||
        return 2
 
-check_todo_list
+git rebase--helper --check-todo-list || {
+       ret=$?
+       checkout_onto
+       exit $ret
+}
 
 expand_todo_ids
 
-test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
+test -d "$rewritten" || test -n "$force_rebase" ||
+onto="$(git rebase--helper --skip-unnecessary-picks)" ||
+die "Could not skip unnecessary pick commands"
 
 checkout_onto
 if test -z "$rebase_root" && test ! -d "$rewritten"
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'});
diff --git a/git.c b/git.c
index f31dca69620ac244e87e04f1d508ee23cf2f92cf..9e96dd409017c643e8a9bebbc41281728978b0b8 100644 (file)
--- a/git.c
+++ b/git.c
@@ -182,6 +182,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1);
                        if (envchanged)
                                *envchanged = 1;
+               } else if (!strcmp(cmd, "--no-optional-locks")) {
+                       setenv(GIT_OPTIONAL_LOCKS_ENVIRONMENT, "0", 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else if (!strcmp(cmd, "--shallow-file")) {
                        (*argv)++;
                        (*argc)--;
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..f3dc218b2a3d2662efda56fb6c8878a37a4e67a4 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);
@@ -486,10 +486,9 @@ static int show_head_ref(const char *refname, const struct object_id *oid,
        struct strbuf *buf = cb_data;
 
        if (flag & REF_ISSYMREF) {
-               struct object_id unused;
                const char *target = resolve_ref_unsafe(refname,
                                                        RESOLVE_REF_READING,
-                                                       unused.hash, NULL);
+                                                       NULL, NULL);
 
                if (target)
                        strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
index e4c9b065ce13ca2c4e522e20f603f192ddcb9306..d860c477c60e7baec79220d9f06330a139d0e035 100644 (file)
@@ -1523,6 +1523,7 @@ static int remote_exists(const char *path)
                break;
        case HTTP_ERROR:
                error("unable to access '%s': %s", url, curl_errorstr);
+               /* fallthrough */
        default:
                ret = -1;
        }
diff --git a/http.c b/http.c
index 9e40a465fdec6467726e980da413478b9cf78492..713525f38ed3f254aed2b0dd65fece7972444072 100644 (file)
--- a/http.c
+++ b/http.c
@@ -638,9 +638,7 @@ static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size,
        switch (type) {
        case CURLINFO_TEXT:
                trace_printf_key(&trace_curl, "== Info: %s", data);
-       default:                /* we ignore unknown types by default */
-               return 0;
-
+               break;
        case CURLINFO_HEADER_OUT:
                text = "=> Send header";
                curl_dump_header(text, (unsigned char *)data, size, DO_FILTER);
@@ -665,6 +663,9 @@ static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size,
                text = "<= Recv SSL data";
                curl_dump_data(text, (unsigned char *)data, size);
                break;
+
+       default:                /* we ignore unknown types by default */
+               return 0;
        }
        return 0;
 }
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
 
index ab0709f9aecaacdfaf44aa31c88873b4ab5d8278..545ad0f28bce0ac0f92a31019b92f7ef8a668356 100644 (file)
@@ -90,7 +90,7 @@ static int range_cmp(const void *_r, const void *_s)
  */
 static void range_set_check_invariants(struct range_set *rs)
 {
-       int i;
+       unsigned int i;
 
        if (!rs)
                return;
@@ -110,8 +110,8 @@ static void range_set_check_invariants(struct range_set *rs)
  */
 void sort_and_merge_range_set(struct range_set *rs)
 {
-       int i;
-       int o = 0; /* output cursor */
+       unsigned int i;
+       unsigned int o = 0; /* output cursor */
 
        QSORT(rs->ranges, rs->nr, range_cmp);
 
@@ -144,7 +144,7 @@ void sort_and_merge_range_set(struct range_set *rs)
 static void range_set_union(struct range_set *out,
                             struct range_set *a, struct range_set *b)
 {
-       int i = 0, j = 0;
+       unsigned int i = 0, j = 0;
        struct range *ra = a->ranges;
        struct range *rb = b->ranges;
        /* cannot make an alias of out->ranges: it may change during grow */
@@ -186,7 +186,7 @@ static void range_set_union(struct range_set *out,
 static void range_set_difference(struct range_set *out,
                                  struct range_set *a, struct range_set *b)
 {
-       int i, j =  0;
+       unsigned int i, j =  0;
        for (i = 0; i < a->nr; i++) {
                long start = a->ranges[i].start;
                long end = a->ranges[i].end;
@@ -397,7 +397,7 @@ static void diff_ranges_filter_touched(struct diff_ranges *out,
                                       struct diff_ranges *diff,
                                       struct range_set *rs)
 {
-       int i, j = 0;
+       unsigned int i, j = 0;
 
        assert(out->target.nr == 0);
 
@@ -426,7 +426,7 @@ static void range_set_shift_diff(struct range_set *out,
                                 struct range_set *rs,
                                 struct diff_ranges *diff)
 {
-       int i, j = 0;
+       unsigned int i, j = 0;
        long offset = 0;
        struct range *src = rs->ranges;
        struct range *target = diff->target.ranges;
@@ -873,7 +873,7 @@ static char *output_prefix(struct diff_options *opt)
 
 static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
 {
-       int i, j = 0;
+       unsigned int i, j = 0;
        long p_lines, t_lines;
        unsigned long *p_ends = NULL, *t_ends = NULL;
        struct diff_filepair *pair = range->pair;
@@ -906,7 +906,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
                long t_start = range->ranges.ranges[i].start;
                long t_end = range->ranges.ranges[i].end;
                long t_cur = t_start;
-               int j_last;
+               unsigned int j_last;
 
                while (j < diff->target.nr && diff->target.ranges[j].end < t_start)
                        j++;
index 7a5c24e2df40c09274077928574bc91349d475d1..e2a5ee7c6d077df353a20d2d55d8b4fc24642bc3 100644 (file)
@@ -14,7 +14,7 @@ struct range {
 
 /* A set of ranges.  The ranges must always be disjoint and sorted. */
 struct range_set {
-       int alloc, nr;
+       unsigned int alloc, nr;
        struct range *ranges;
 };
 
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 410ab4f02de5b532485c46b5cceffc3c677df214..cea056234d0a0b605c988a4e157852cd659f22ef 100644 (file)
@@ -185,7 +185,6 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
 {
        const struct name_decoration *list, *head = NULL;
        const char *branch_name = NULL;
-       struct object_id unused;
        int rru_flags;
 
        /* First find HEAD */
@@ -198,7 +197,7 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
                return NULL;
 
        /* Now resolve and find the matching current branch */
-       branch_name = resolve_ref_unsafe("HEAD", 0, unused.hash, &rru_flags);
+       branch_name = resolve_ref_unsafe("HEAD", 0, NULL, &rru_flags);
        if (!(rru_flags & REF_ISSYMREF))
                return NULL;
 
index bd574cb75210334b1a4628f182743b49bd389cd7..a89db22ab0c8ef50ae31d5811de5fc48d09224a2 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);
 }
 
@@ -368,11 +367,16 @@ static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
 
        while ((c = *in++) != 0) {
                if (c == '=') {
-                       int d = *in++;
+                       int ch, d = *in;
                        if (d == '\n' || !d)
                                break; /* drop trailing newline */
-                       strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
-                       continue;
+                       ch = hex2chr(in);
+                       if (ch >= 0) {
+                               strbuf_addch(out, ch);
+                               in += 2;
+                               continue;
+                       }
+                       /* garbage -- fall through */
                }
                if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
                        c = 0x20;
@@ -823,6 +827,7 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
                if (!handle_commit_msg(mi, line))
                        break;
                mi->filter_stage++;
+               /* fallthrough */
        case 1:
                handle_patch(mi, line);
                break;
@@ -929,6 +934,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..4352c34a6ee50f907f2d03e7d0ed1a596345a5d3 100644 (file)
@@ -302,14 +302,12 @@ 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)
                                break;
                        die_errno("notes-merge");
-               } else if (!ret) {
-                       die("notes-merge: disk full?");
                }
                size -= ret;
                buf += ret;
@@ -624,7 +622,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++);
index 321d7e9201b2ba6c8b39592a591786409a87edb1..b9a4a0e50172fb169226786b17134c6ab0627333 100644 (file)
--- a/object.c
+++ b/object.c
@@ -353,6 +353,19 @@ static void object_array_release_entry(struct object_array_entry *ent)
        free(ent->path);
 }
 
+struct object *object_array_pop(struct object_array *array)
+{
+       struct object *ret;
+
+       if (!array->nr)
+               return NULL;
+
+       ret = array->objects[array->nr - 1].item;
+       object_array_release_entry(&array->objects[array->nr - 1]);
+       array->nr--;
+       return ret;
+}
+
 void object_array_filter(struct object_array *array,
                         object_array_each_func_t want, void *cb_data)
 {
index 0a419ba8da52453ebbd202f50d5c2aa1c9aea2f0..df8abe96f7da2585f7f07c68f53695a2d450e2ae 100644 (file)
--- a/object.h
+++ b/object.h
@@ -116,6 +116,14 @@ int object_list_contains(struct object_list *list, struct object *obj);
 void add_object_array(struct object *obj, const char *name, struct object_array *array);
 void add_object_array_with_path(struct object *obj, const char *name, struct object_array *array, unsigned mode, const char *path);
 
+/*
+ * Returns NULL if the array is empty. Otherwise, returns the last object
+ * after removing its entry from the array. Other resources associated
+ * with that object are left in an unspecified state and should not be
+ * examined.
+ */
+struct object *object_array_pop(struct object_array *array);
+
 typedef int (*object_array_each_func_t)(struct object_array_entry *, void *);
 
 /*
diff --git a/outgoing/packfile.h b/outgoing/packfile.h
deleted file mode 100644 (file)
index e69de29..0000000
index 8e47a96b3bb68f8d5ccb87f1c955863aeda39bf6..a8df5ce2ab67bfdb5445e4c02e4ad65032abe9a0 100644 (file)
@@ -297,9 +297,7 @@ void bitmap_writer_build(struct packing_data *to_pack)
 
                        traverse_commit_list(&revs, show_commit, show_object, base);
 
-                       revs.pending.nr = 0;
-                       revs.pending.alloc = 0;
-                       revs.pending.objects = NULL;
+                       object_array_clear(&revs.pending);
 
                        stored->bitmap = bitmap_to_ewah(base);
                        need_reset = 0;
index cb3d14ba45a94dab5fee3b2ebc52eb379506313e..42e3d5f4f26ee0f31d13c2a3d15bc31da5f7c1d4 100644 (file)
@@ -654,8 +654,6 @@ static int in_bitmapped_pack(struct object_list *roots)
 int prepare_bitmap_walk(struct rev_info *revs)
 {
        unsigned int i;
-       unsigned int pending_nr = revs->pending.nr;
-       struct object_array_entry *pending_e = revs->pending.objects;
 
        struct object_list *wants = NULL;
        struct object_list *haves = NULL;
@@ -670,8 +668,8 @@ int prepare_bitmap_walk(struct rev_info *revs)
                        return -1;
        }
 
-       for (i = 0; i < pending_nr; ++i) {
-               struct object *object = pending_e[i].item;
+       for (i = 0; i < revs->pending.nr; ++i) {
+               struct object *object = revs->pending.objects[i].item;
 
                if (object->type == OBJ_NONE)
                        parse_object_or_die(&object->oid, NULL);
@@ -715,9 +713,7 @@ int prepare_bitmap_walk(struct rev_info *revs)
        if (!bitmap_git.loaded && load_pack_bitmap() < 0)
                return -1;
 
-       revs->pending.nr = 0;
-       revs->pending.alloc = 0;
-       revs->pending.objects = NULL;
+       object_array_clear(&revs->pending);
 
        if (haves) {
                revs->ignore_missing_links = 1;
index a333ec675476b4f10a5e12fe3303333ea200faea..fea62841920c9647edfcfba249a59bfb91170d8e 100644 (file)
@@ -213,14 +213,19 @@ void fixup_pack_header_footer(int pack_fd,
        git_SHA_CTX old_sha1_ctx, new_sha1_ctx;
        struct pack_header hdr;
        char *buf;
+       ssize_t read_result;
 
        git_SHA1_Init(&old_sha1_ctx);
        git_SHA1_Init(&new_sha1_ctx);
 
        if (lseek(pack_fd, 0, SEEK_SET) != 0)
                die_errno("Failed seeking to start of '%s'", pack_name);
-       if (read_in_full(pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+       read_result = read_in_full(pack_fd, &hdr, sizeof(hdr));
+       if (read_result < 0)
                die_errno("Unable to reread header of '%s'", pack_name);
+       else if (read_result != sizeof(hdr))
+               die_errno("Unexpected short read for header of '%s'",
+                         pack_name);
        if (lseek(pack_fd, 0, SEEK_SET) != 0)
                die_errno("Failed seeking to start of '%s'", pack_name);
        git_SHA1_Update(&old_sha1_ctx, &hdr, sizeof(hdr));
index f86fa051c9e67def994b8b487ad34f200967d5e9..eab7542487a194ecd25708f906e66c40e1bed336 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; }
@@ -444,6 +442,7 @@ static int open_packed_git_1(struct packed_git *p)
        unsigned char sha1[20];
        unsigned char *idx_sha1;
        long fd_flag;
+       ssize_t read_result;
 
        if (!p->index_data && open_pack_index(p))
                return error("packfile %s index unavailable", p->pack_name);
@@ -485,7 +484,10 @@ static int open_packed_git_1(struct packed_git *p)
                return error("cannot set FD_CLOEXEC");
 
        /* Verify we recognize this pack file format. */
-       if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+       read_result = read_in_full(p->pack_fd, &hdr, sizeof(hdr));
+       if (read_result < 0)
+               return error_errno("error reading from %s", p->pack_name);
+       if (read_result != sizeof(hdr))
                return error("file %s is far too short to be a packfile", p->pack_name);
        if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
                return error("file %s is not a GIT packfile", p->pack_name);
@@ -502,7 +504,10 @@ static int open_packed_git_1(struct packed_git *p)
                             p->num_objects);
        if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
                return error("end of packfile %s is unavailable", p->pack_name);
-       if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
+       read_result = read_in_full(p->pack_fd, sha1, sizeof(sha1));
+       if (read_result < 0)
+               return error_errno("error reading from %s", p->pack_name);
+       if (read_result != sizeof(sha1))
                return error("packfile %s signature is unavailable", p->pack_name);
        idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
        if (hashcmp(sha1, idx_sha1))
@@ -861,9 +866,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 +1837,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 0dd9fc6a0dd0a518200d9bbd834decb3c3ee22c6..fca7159646c82cb9213e72afd94d8debc6bd4c51 100644 (file)
@@ -581,6 +581,7 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                                       const struct option *opts, int full, int err)
 {
        FILE *outfile = err ? stderr : stdout;
+       int need_newline;
 
        if (!usagestr)
                return PARSE_OPT_HELP;
@@ -599,12 +600,11 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                if (**usagestr)
                        fprintf_ln(outfile, _("    %s"), _(*usagestr));
                else
-                       putchar('\n');
+                       fputc('\n', outfile);
                usagestr++;
        }
 
-       if (opts->type != OPTION_GROUP)
-               fputc('\n', outfile);
+       need_newline = 1;
 
        for (; opts->type != OPTION_END; opts++) {
                size_t pos;
@@ -612,6 +612,7 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
 
                if (opts->type == OPTION_GROUP) {
                        fputc('\n', outfile);
+                       need_newline = 0;
                        if (*opts->help)
                                fprintf(outfile, "%s\n", _(opts->help));
                        continue;
@@ -619,6 +620,11 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                if (!full && (opts->flags & PARSE_OPT_HIDDEN))
                        continue;
 
+               if (need_newline) {
+                       fputc('\n', outfile);
+                       need_newline = 0;
+               }
+
                pos = fprintf(outfile, "    ");
                if (opts->short_name) {
                        if (opts->flags & PARSE_OPT_NODASH)
diff --git a/path.c b/path.c
index b533ec938dd18320beb533969b0763992d4d3a85..5aa9244eb24d95717f2b20e207266c289c925757 100644 (file)
--- a/path.c
+++ b/path.c
@@ -637,8 +637,9 @@ void strbuf_git_common_path(struct strbuf *sb,
 int validate_headref(const char *path)
 {
        struct stat st;
-       char *buf, buffer[256];
-       unsigned char sha1[20];
+       char buffer[256];
+       const char *refname;
+       struct object_id oid;
        int fd;
        ssize_t len;
 
@@ -662,24 +663,24 @@ int validate_headref(const char *path)
        len = read_in_full(fd, buffer, sizeof(buffer)-1);
        close(fd);
 
+       if (len < 0)
+               return -1;
+       buffer[len] = '\0';
+
        /*
         * Is it a symbolic ref?
         */
-       if (len < 4)
-               return -1;
-       if (!memcmp("ref:", buffer, 4)) {
-               buf = buffer + 4;
-               len -= 4;
-               while (len && isspace(*buf))
-                       buf++, len--;
-               if (len >= 5 && !memcmp("refs/", buf, 5))
+       if (skip_prefix(buffer, "ref:", &refname)) {
+               while (isspace(*refname))
+                       refname++;
+               if (starts_with(refname, "refs/"))
                        return 0;
        }
 
        /*
         * Is this a detached HEAD?
         */
-       if (!get_sha1_hex(buffer, sha1))
+       if (!get_oid_hex(buffer, &oid))
                return 0;
 
        return -1;
index e2a23ebc9668b066d337391e96139af49907edfa..cdefdc7cc0e0be248297d1191eefe72418923f7a 100644 (file)
@@ -526,10 +526,6 @@ 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)
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..93ea311443a37b81b85a72dc569c715417d7ef29 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, ...)
@@ -258,7 +258,7 @@ static int get_packet_data(int fd, char **src_buf, size_t *src_size,
        }
 
        /* And complain if we didn't get enough bytes to satisfy the read. */
-       if (ret < size) {
+       if (ret != size) {
                if (options & PACKET_READ_GENTLE_ON_EOF)
                        return -1;
 
diff --git a/quote.c b/quote.c
index 53b98a5b840d8fd4d73fab27348b657501923dca..de2922ddd63d6fc001822d116387c8ced7d7630d 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -431,6 +431,7 @@ void tcl_quote_buf(struct strbuf *sb, const char *src)
                case '{': case '}':
                case '$': case '\\': case '"':
                        strbuf_addch(sb, '\\');
+                       /* fallthrough */
                default:
                        strbuf_addch(sb, c);
                        break;
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..65f4fe8375d59234d5e58f87a8593fc50e6336a2 100644 (file)
@@ -220,6 +220,7 @@ static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
        case S_IFDIR:
                if (S_ISGITLINK(ce->ce_mode))
                        return ce_compare_gitlink(ce) ? DATA_CHANGED : 0;
+               /* else fallthrough */
        default:
                return TYPE_CHANGED;
        }
@@ -1922,7 +1923,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 +1972,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 +1981,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 +2104,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 +2312,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 +2340,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 +2348,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 +2423,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 c5e6f79c77d33eb00ef05a373bb35e910e3b7820..df075fcd06b0826982c94f27ab38395ac7ce30a6 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -239,8 +239,7 @@ int read_ref(const char *refname, unsigned char *sha1)
 
 int ref_exists(const char *refname)
 {
-       unsigned char sha1[20];
-       return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
+       return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL);
 }
 
 static int filter_refs(const char *refname, const struct object_id *oid,
@@ -286,12 +285,11 @@ static int warn_if_dangling_symref(const char *refname, const struct object_id *
 {
        struct warn_if_dangling_data *d = cb_data;
        const char *resolves_to;
-       struct object_id junk;
 
        if (!(flags & REF_ISSYMREF))
                return 0;
 
-       resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);
+       resolves_to = resolve_ref_unsafe(refname, 0, NULL, NULL);
        if (!resolves_to
            || (d->refname
                ? strcmp(resolves_to, d->refname)
@@ -336,12 +334,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 +344,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 +354,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 +592,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 +607,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 +937,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 +1248,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 +1262,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(
@@ -1348,11 +1324,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)
 {
@@ -1374,23 +1345,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)
@@ -1398,7 +1361,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)
@@ -1437,9 +1400,12 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    unsigned char *sha1, int *flags)
 {
        static struct strbuf sb_refname = STRBUF_INIT;
+       struct object_id unused_oid;
        int unused_flags;
        int symref_count;
 
+       if (!sha1)
+               sha1 = unused_oid.hash;
        if (!flags)
                flags = &unused_flags;
 
@@ -1525,25 +1491,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;
@@ -1658,31 +1609,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,
@@ -1690,7 +1642,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;
 }
 
@@ -2101,3 +2056,14 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
 {
        return refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);
 }
+
+int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
+                   const char *newref, const char *logmsg)
+{
+       return refs->be->copy_ref(refs, oldref, newref, logmsg);
+}
+
+int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+       return refs_copy_existing_ref(get_main_ref_store(), oldref, newref, logmsg);
+}
diff --git a/refs.h b/refs.h
index 6daa78eb50ddc195c6d5bfb568d0b05ab08f7923..a02b628c8fe81b648f13c66b7e618d8660151e77 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -10,10 +10,11 @@ struct worktree;
 /*
  * Resolve a reference, recursively following symbolic refererences.
  *
- * Store the referred-to object's name in sha1 and return the name of
- * the non-symbolic reference that ultimately pointed at it.  The
- * return value, if not NULL, is a pointer into either a static buffer
- * or the input ref.
+ * Return the name of the non-symbolic reference that ultimately pointed
+ * at the resolved object name.  The return value, if not NULL, is a
+ * pointer into either a static buffer or the input ref.
+ *
+ * If sha1 is non-NULL, store the referred-to object's name in it.
  *
  * If the reference cannot be resolved to an object, the behavior
  * depends on the RESOLVE_REF_READING flag:
@@ -275,6 +276,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 +292,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 +305,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 +345,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.
  */
@@ -443,7 +442,14 @@ char *shorten_unambiguous_ref(const char *refname, int strict);
 /** rename ref, return 0 on success **/
 int refs_rename_ref(struct ref_store *refs, const char *oldref,
                    const char *newref, const char *logmsg);
-int rename_ref(const char *oldref, const char *newref, const char *logmsg);
+int rename_ref(const char *oldref, const char *newref,
+                       const char *logmsg);
+
+/** copy ref, return 0 on success **/
+int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
+                   const char *newref, const char *logmsg);
+int copy_existing_ref(const char *oldref, const char *newref,
+                       const char *logmsg);
 
 int refs_create_symref(struct ref_store *refs, const char *refname,
                       const char *target, const char *logmsg);
index 7d12de88d015a73ce8cc77da5e01ad1a43086025..1b6141e718a5c449226c22fdeefb80b591ca3f7e 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) {
                        /*
@@ -914,11 +900,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;
@@ -1239,9 +1223,9 @@ static int commit_ref_update(struct files_ref_store *refs,
                             const struct object_id *oid, const char *logmsg,
                             struct strbuf *err);
 
-static int files_rename_ref(struct ref_store *ref_store,
+static int files_copy_or_rename_ref(struct ref_store *ref_store,
                            const char *oldrefname, const char *newrefname,
-                           const char *logmsg)
+                           const char *logmsg, int copy)
 {
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
@@ -1273,8 +1257,12 @@ static int files_rename_ref(struct ref_store *ref_store,
        }
 
        if (flag & REF_ISSYMREF) {
-               ret = error("refname %s is a symbolic ref, renaming it is not supported",
-                           oldrefname);
+               if (copy)
+                       ret = error("refname %s is a symbolic ref, copying it is not supported",
+                                   oldrefname);
+               else
+                       ret = error("refname %s is a symbolic ref, renaming it is not supported",
+                                   oldrefname);
                goto out;
        }
        if (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {
@@ -1282,13 +1270,19 @@ static int files_rename_ref(struct ref_store *ref_store,
                goto out;
        }
 
-       if (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {
+       if (!copy && log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {
                ret = error("unable to move logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
                            oldrefname, strerror(errno));
                goto out;
        }
 
-       if (refs_delete_ref(&refs->base, logmsg, oldrefname,
+       if (copy && log && copy_file(tmp_renamed_log.buf, sb_oldref.buf, 0644)) {
+               ret = error("unable to copy logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
+                           oldrefname, strerror(errno));
+               goto out;
+       }
+
+       if (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname,
                            orig_oid.hash, REF_NODEREF)) {
                error("unable to delete old %s", oldrefname);
                goto rollback;
@@ -1301,7 +1295,7 @@ static int files_rename_ref(struct ref_store *ref_store,
         * the safety anyway; we want to delete the reference whatever
         * its current value.
         */
-       if (!refs_read_ref_full(&refs->base, newrefname,
+       if (!copy && !refs_read_ref_full(&refs->base, newrefname,
                                RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
                                oid.hash, NULL) &&
            refs_delete_ref(&refs->base, NULL, newrefname,
@@ -1332,7 +1326,10 @@ static int files_rename_ref(struct ref_store *ref_store,
        lock = lock_ref_sha1_basic(refs, newrefname, NULL, NULL, NULL,
                                   REF_NODEREF, NULL, &err);
        if (!lock) {
-               error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
+               if (copy)
+                       error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf);
+               else
+                       error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
                strbuf_release(&err);
                goto rollback;
        }
@@ -1383,16 +1380,32 @@ static int files_rename_ref(struct ref_store *ref_store,
        return ret;
 }
 
-static int close_ref(struct ref_lock *lock)
+static int files_rename_ref(struct ref_store *ref_store,
+                           const char *oldrefname, const char *newrefname,
+                           const char *logmsg)
+{
+       return files_copy_or_rename_ref(ref_store, oldrefname,
+                                newrefname, logmsg, 0);
+}
+
+static int files_copy_ref(struct ref_store *ref_store,
+                           const char *oldrefname, const char *newrefname,
+                           const char *logmsg)
+{
+       return files_copy_or_rename_ref(ref_store, oldrefname,
+                                newrefname, logmsg, 1);
+}
+
+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)) {
@@ -1416,7 +1429,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;
 }
@@ -1530,7 +1543,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;
@@ -1608,12 +1621,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;
        }
@@ -1657,13 +1670,12 @@ static int commit_ref_update(struct files_ref_store *refs,
                 * check with HEAD only which should cover 99% of all usage
                 * scenarios (even 100% of the default ones).
                 */
-               struct object_id head_oid;
                int head_flag;
                const char *head_ref;
 
                head_ref = refs_resolve_ref_unsafe(&refs->base, "HEAD",
                                                   RESOLVE_REF_READING,
-                                                  head_oid.hash, &head_flag);
+                                                  NULL, &head_flag);
                if (head_ref && (head_flag & REF_ISSYMREF) &&
                    !strcmp(head_ref, lock->ref_name)) {
                        struct strbuf log_err = STRBUF_INIT;
@@ -1690,7 +1702,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);
@@ -1726,14 +1738,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));
@@ -2040,23 +2052,64 @@ 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, 0);
-       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(
+                       0,
+                       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.
@@ -2080,11 +2133,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 "
@@ -2099,6 +2151,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;
@@ -2125,13 +2185,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",
@@ -2166,6 +2225,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;
@@ -2237,7 +2307,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");
@@ -2249,7 +2319,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,
@@ -2263,7 +2333,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;
@@ -2282,10 +2352,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 {
                        /*
@@ -2299,13 +2371,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
@@ -2342,7 +2416,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;
                }
@@ -2353,13 +2428,17 @@ 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 {
@@ -2915,16 +2994,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") < 0 ||
+                           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)",
@@ -2978,6 +3058,7 @@ struct ref_storage_be refs_be_files = {
        files_create_symref,
        files_delete_refs,
        files_rename_ref,
+       files_copy_ref,
 
        files_ref_iterator_begin,
        files_read_raw_ref,
index d500ebfaa5311fc37ae0b35144c8a6bd130b54be..6e85b0bf0bd5efad7617698a4c2f740a9a28fb05 100644 (file)
@@ -143,7 +143,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;
 };
 
 /*
@@ -997,8 +997,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;
        }
 
@@ -1091,7 +1092,8 @@ static int write_with_updates(struct packed_ref_store *refs,
        packed_refs_path = get_locked_file_path(&refs->lock);
        strbuf_addf(&sb, "%s.new", packed_refs_path);
        free(packed_refs_path);
-       if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
+       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);
@@ -1099,7 +1101,7 @@ static int write_with_updates(struct packed_ref_store *refs,
        }
        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));
@@ -1236,11 +1238,12 @@ static int write_with_updates(struct packed_ref_store *refs,
                goto error;
        }
 
-       if (close_tempfile(&refs->tempfile)) {
+       if (close_tempfile_gently(refs->tempfile)) {
                strbuf_addf(err, "error closing file %s: %s",
-                           get_tempfile_path(&refs->tempfile),
+                           get_tempfile_path(refs->tempfile),
                            strerror(errno));
                strbuf_release(&sb);
+               delete_tempfile(&refs->tempfile);
                return -1;
        }
 
@@ -1248,7 +1251,7 @@ static int write_with_updates(struct packed_ref_store *refs,
 
 write_error:
        strbuf_addf(err, "error writing to %s: %s",
-                   get_tempfile_path(&refs->tempfile), strerror(errno));
+                   get_tempfile_path(refs->tempfile), strerror(errno));
 
 error:
        if (iter)
@@ -1273,7 +1276,7 @@ static void packed_transaction_cleanup(struct packed_ref_store *refs,
        if (data) {
                string_list_clear(&data->updates, 0);
 
-               if (is_tempfile_active(&refs->tempfile))
+               if (is_tempfile_active(refs->tempfile))
                        delete_tempfile(&refs->tempfile);
 
                if (data->own_lock && is_lock_file_locked(&refs->lock)) {
@@ -1469,6 +1472,13 @@ static int packed_rename_ref(struct ref_store *ref_store,
        die("BUG: packed reference store does not support renaming references");
 }
 
+static int packed_copy_ref(struct ref_store *ref_store,
+                          const char *oldrefname, const char *newrefname,
+                          const char *logmsg)
+{
+       die("BUG: packed reference store does not support copying references");
+}
+
 static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store)
 {
        return empty_ref_iterator_begin();
@@ -1533,6 +1543,7 @@ struct ref_storage_be refs_be_packed = {
        packed_create_symref,
        packed_delete_refs,
        packed_rename_ref,
+       packed_copy_ref,
 
        packed_ref_iterator_begin,
        packed_read_raw_ref,
index cc6c373f596a7750c8cc0b1729abfa736217c2b1..448de4bccb8f3d9531b50d5489af001ec7ce4732 100644 (file)
@@ -571,6 +571,9 @@ typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg,
 typedef int rename_ref_fn(struct ref_store *ref_store,
                          const char *oldref, const char *newref,
                          const char *logmsg);
+typedef int copy_ref_fn(struct ref_store *ref_store,
+                         const char *oldref, const char *newref,
+                         const char *logmsg);
 
 /*
  * Iterate over the references in `ref_store` whose names start with
@@ -669,6 +672,7 @@ struct ref_storage_be {
        create_symref_fn *create_symref;
        delete_refs_fn *delete_refs;
        rename_ref_fn *rename_ref;
+       copy_ref_fn *copy_ref;
 
        ref_iterator_begin_fn *iterator_begin;
        read_raw_ref_fn *read_raw_ref;
index 43c317e4e9f6e67aa3e1d0ae245d097bdba664db..b220f0dfc619a6d3150cb2bcee00114f76bd0223 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -466,7 +466,6 @@ static void alias_all_urls(void)
 static void read_config(void)
 {
        static int loaded;
-       struct object_id oid;
        int flag;
 
        if (loaded)
@@ -475,7 +474,7 @@ static void read_config(void)
 
        current_branch = NULL;
        if (startup_info->have_repository) {
-               const char *head_ref = resolve_ref_unsafe("HEAD", 0, oid.hash, &flag);
+               const char *head_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flag);
                if (head_ref && (flag & REF_ISSYMREF) &&
                    skip_prefix(head_ref, "refs/heads/", &head_ref)) {
                        current_branch = make_branch(head_ref, 0);
@@ -1105,10 +1104,9 @@ static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 static char *guess_ref(const char *name, struct ref *peer)
 {
        struct strbuf buf = STRBUF_INIT;
-       struct object_id oid;
 
        const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
-                                          oid.hash, NULL);
+                                          NULL, NULL);
        if (!r)
                return NULL;
 
@@ -1166,12 +1164,11 @@ static int match_explicit(struct ref *src, struct ref *dst,
                return -1;
 
        if (!dst_value) {
-               struct object_id oid;
                int flag;
 
                dst_value = resolve_ref_unsafe(matched_src->name,
                                               RESOLVE_REF_READING,
-                                              oid.hash, &flag);
+                                              NULL, &flag);
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
                     !starts_with(dst_value, "refs/heads/")))
@@ -1792,10 +1789,9 @@ const char *branch_get_push(struct branch *branch, struct strbuf *err)
 
 static int ignore_symref_update(const char *refname)
 {
-       struct object_id oid;
        int flag;
 
-       if (!resolve_ref_unsafe(refname, 0, oid.hash, &flag))
+       if (!resolve_ref_unsafe(refname, 0, NULL, &flag))
                return 0; /* non-existing refs are OK */
        return (flag & REF_ISSYMREF);
 }
@@ -2084,7 +2080,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..d167223e694e54626786740954454d4973e28752 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;
 
@@ -1104,7 +1106,7 @@ static void add_rev_cmdline(struct rev_info *revs,
                            unsigned flags)
 {
        struct rev_cmdline_info *info = &revs->cmdline;
-       int nr = info->nr;
+       unsigned int nr = info->nr;
 
        ALLOC_GROW(info->rev, nr + 1, info->alloc);
        info->rev[nr].item = item;
@@ -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;
        }
@@ -2178,11 +2248,10 @@ static int handle_revision_pseudo_opt(const char *submodule,
 
 static void NORETURN diagnose_missing_default(const char *def)
 {
-       unsigned char sha1[20];
        int flags;
        const char *refname;
 
-       refname = resolve_ref_unsafe(def, 0, sha1, &flags);
+       refname = resolve_ref_unsafe(def, 0, NULL, &flags);
        if (!refname || !(flags & REF_ISSYMREF) || (flags & REF_ISBROKEN))
                die(_("your current branch appears to be broken"));
 
@@ -2201,10 +2270,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 +2288,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 +2349,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 +2371,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..54761200adf2d5111b8aba097299bd2fd080a949 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,
@@ -149,6 +150,17 @@ struct rev_info {
                        date_mode_explicit:1,
                        preserve_subject:1;
        unsigned int    disable_stdin:1;
+       /*
+        * Set `leak_pending` to prevent `prepare_revision_walk()` from clearing
+        * the array of pending objects (`pending`). It will still forget about
+        * the array and its entries, so they really are leaked. This can be
+        * useful if the `struct object_array` `pending` is copied before
+        * calling `prepare_revision_walk()`. By setting `leak_pending`, you
+        * effectively claim ownership of the old array, so you should most
+        * likely call `object_array_clear(&pending_copy)` once you are done.
+        * Observe that this is about ownership of the array and its entries,
+        * not the commits referenced by those entries.
+        */
        unsigned int    leak_pending:1;
        /* --show-linear-break */
        unsigned int    track_linear:1,
index 11d6f3d98398e8b7a1add501dd7ee85a736531b6..a8cc6b266e67847abafa613184752b3b781eb8dd 100644 (file)
@@ -492,9 +492,12 @@ 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. */
+                       }
+                       /* else fallthrough */
                default:
                        continue;
                }
index fcceabb80f4261006cdd65bc0ec95ac54ea42e7c..b8c1e876fa7b57986e6ae93873e23f87c45b9e49 100644 (file)
@@ -20,6 +20,7 @@
 #include "trailer.h"
 #include "log-tree.h"
 #include "wt-status.h"
+#include "hashmap.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -1565,6 +1566,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 +1574,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());
@@ -2432,3 +2436,533 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
 
        strbuf_release(&sob);
 }
+
+int sequencer_make_script(int keep_empty, FILE *out,
+               int argc, const char **argv)
+{
+       char *format = NULL;
+       struct pretty_print_context pp = {0};
+       struct strbuf buf = STRBUF_INIT;
+       struct rev_info revs;
+       struct commit *commit;
+
+       init_revisions(&revs, NULL);
+       revs.verbose_header = 1;
+       revs.max_parents = 1;
+       revs.cherry_pick = 1;
+       revs.limited = 1;
+       revs.reverse = 1;
+       revs.right_only = 1;
+       revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+       revs.topo_order = 1;
+
+       revs.pretty_given = 1;
+       git_config_get_string("rebase.instructionFormat", &format);
+       if (!format || !*format) {
+               free(format);
+               format = xstrdup("%s");
+       }
+       get_commit_format(format, &revs);
+       free(format);
+       pp.fmt = revs.commit_format;
+       pp.output_encoding = get_log_output_encoding();
+
+       if (setup_revisions(argc, argv, &revs, NULL) > 1)
+               return error(_("make_script: unhandled options"));
+
+       if (prepare_revision_walk(&revs) < 0)
+               return error(_("make_script: error preparing revisions"));
+
+       while ((commit = get_revision(&revs))) {
+               strbuf_reset(&buf);
+               if (!keep_empty && is_original_commit_empty(commit))
+                       strbuf_addf(&buf, "%c ", comment_line_char);
+               strbuf_addf(&buf, "pick %s ", oid_to_hex(&commit->object.oid));
+               pretty_print_commit(&pp, commit, &buf);
+               strbuf_addch(&buf, '\n');
+               fputs(buf.buf, out);
+       }
+       strbuf_release(&buf);
+       return 0;
+}
+
+
+int transform_todo_ids(int shorten_ids)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       int fd, res, i;
+       FILE *out;
+
+       strbuf_reset(&todo_list.buf);
+       fd = open(todo_file, O_RDONLY);
+       if (fd < 0)
+               return error_errno(_("could not open '%s'"), todo_file);
+       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
+               close(fd);
+               return error(_("could not read '%s'."), todo_file);
+       }
+       close(fd);
+
+       res = parse_insn_buffer(todo_list.buf.buf, &todo_list);
+       if (res) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+
+       out = fopen(todo_file, "w");
+       if (!out) {
+               todo_list_release(&todo_list);
+               return error(_("unable to open '%s' for writing"), todo_file);
+       }
+       for (i = 0; i < todo_list.nr; i++) {
+               struct todo_item *item = todo_list.items + i;
+               int bol = item->offset_in_buf;
+               const char *p = todo_list.buf.buf + bol;
+               int eol = i + 1 < todo_list.nr ?
+                       todo_list.items[i + 1].offset_in_buf :
+                       todo_list.buf.len;
+
+               if (item->command >= TODO_EXEC && item->command != TODO_DROP)
+                       fwrite(p, eol - bol, 1, out);
+               else {
+                       const char *id = shorten_ids ?
+                               short_commit_name(item->commit) :
+                               oid_to_hex(&item->commit->object.oid);
+                       int len;
+
+                       p += strspn(p, " \t"); /* left-trim command */
+                       len = strcspn(p, " \t"); /* length of command */
+
+                       fprintf(out, "%.*s %s %.*s\n",
+                               len, p, id, item->arg_len, item->arg);
+               }
+       }
+       fclose(out);
+       todo_list_release(&todo_list);
+       return 0;
+}
+
+enum check_level {
+       CHECK_IGNORE = 0, CHECK_WARN, CHECK_ERROR
+};
+
+static enum check_level get_missing_commit_check_level(void)
+{
+       const char *value;
+
+       if (git_config_get_value("rebase.missingcommitscheck", &value) ||
+                       !strcasecmp("ignore", value))
+               return CHECK_IGNORE;
+       if (!strcasecmp("warn", value))
+               return CHECK_WARN;
+       if (!strcasecmp("error", value))
+               return CHECK_ERROR;
+       warning(_("unrecognized setting %s for option"
+                 "rebase.missingCommitsCheck. Ignoring."), value);
+       return CHECK_IGNORE;
+}
+
+/*
+ * Check if the user dropped some commits by mistake
+ * Behaviour determined by rebase.missingCommitsCheck.
+ * Check if there is an unrecognized command or a
+ * bad SHA-1 in a command.
+ */
+int check_todo_list(void)
+{
+       enum check_level check_level = get_missing_commit_check_level();
+       struct strbuf todo_file = STRBUF_INIT;
+       struct todo_list todo_list = TODO_LIST_INIT;
+       struct strbuf missing = STRBUF_INIT;
+       int advise_to_edit_todo = 0, res = 0, fd, i;
+
+       strbuf_addstr(&todo_file, rebase_path_todo());
+       fd = open(todo_file.buf, O_RDONLY);
+       if (fd < 0) {
+               res = error_errno(_("could not open '%s'"), todo_file.buf);
+               goto leave_check;
+       }
+       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
+               close(fd);
+               res = error(_("could not read '%s'."), todo_file.buf);
+               goto leave_check;
+       }
+       close(fd);
+       advise_to_edit_todo = res =
+               parse_insn_buffer(todo_list.buf.buf, &todo_list);
+
+       if (res || check_level == CHECK_IGNORE)
+               goto leave_check;
+
+       /* Mark the commits in git-rebase-todo as seen */
+       for (i = 0; i < todo_list.nr; i++) {
+               struct commit *commit = todo_list.items[i].commit;
+               if (commit)
+                       commit->util = (void *)1;
+       }
+
+       todo_list_release(&todo_list);
+       strbuf_addstr(&todo_file, ".backup");
+       fd = open(todo_file.buf, O_RDONLY);
+       if (fd < 0) {
+               res = error_errno(_("could not open '%s'"), todo_file.buf);
+               goto leave_check;
+       }
+       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
+               close(fd);
+               res = error(_("could not read '%s'."), todo_file.buf);
+               goto leave_check;
+       }
+       close(fd);
+       strbuf_release(&todo_file);
+       res = !!parse_insn_buffer(todo_list.buf.buf, &todo_list);
+
+       /* Find commits in git-rebase-todo.backup yet unseen */
+       for (i = todo_list.nr - 1; i >= 0; i--) {
+               struct todo_item *item = todo_list.items + i;
+               struct commit *commit = item->commit;
+               if (commit && !commit->util) {
+                       strbuf_addf(&missing, " - %s %.*s\n",
+                                   short_commit_name(commit),
+                                   item->arg_len, item->arg);
+                       commit->util = (void *)1;
+               }
+       }
+
+       /* Warn about missing commits */
+       if (!missing.len)
+               goto leave_check;
+
+       if (check_level == CHECK_ERROR)
+               advise_to_edit_todo = res = 1;
+
+       fprintf(stderr,
+               _("Warning: some commits may have been dropped accidentally.\n"
+               "Dropped commits (newer to older):\n"));
+
+       /* Make the list user-friendly and display */
+       fputs(missing.buf, stderr);
+       strbuf_release(&missing);
+
+       fprintf(stderr, _("To avoid this message, use \"drop\" to "
+               "explicitly remove a commit.\n\n"
+               "Use 'git config rebase.missingCommitsCheck' to change "
+               "the level of warnings.\n"
+               "The possible behaviours are: ignore, warn, error.\n\n"));
+
+leave_check:
+       strbuf_release(&todo_file);
+       todo_list_release(&todo_list);
+
+       if (advise_to_edit_todo)
+               fprintf(stderr,
+                       _("You can fix this with 'git rebase --edit-todo' "
+                         "and then run 'git rebase --continue'.\n"
+                         "Or you can abort the rebase with 'git rebase"
+                         " --abort'.\n"));
+
+       return res;
+}
+
+/* skip picking commits whose parents are unchanged */
+int skip_unnecessary_picks(void)
+{
+       const char *todo_file = rebase_path_todo();
+       struct strbuf buf = STRBUF_INIT;
+       struct todo_list todo_list = TODO_LIST_INIT;
+       struct object_id onto_oid, *oid = &onto_oid, *parent_oid;
+       int fd, i;
+
+       if (!read_oneliner(&buf, rebase_path_onto(), 0))
+               return error(_("could not read 'onto'"));
+       if (get_oid(buf.buf, &onto_oid)) {
+               strbuf_release(&buf);
+               return error(_("need a HEAD to fixup"));
+       }
+       strbuf_release(&buf);
+
+       fd = open(todo_file, O_RDONLY);
+       if (fd < 0) {
+               return error_errno(_("could not open '%s'"), todo_file);
+       }
+       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
+               close(fd);
+               return error(_("could not read '%s'."), todo_file);
+       }
+       close(fd);
+       if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {
+               todo_list_release(&todo_list);
+               return -1;
+       }
+
+       for (i = 0; i < todo_list.nr; i++) {
+               struct todo_item *item = todo_list.items + i;
+
+               if (item->command >= TODO_NOOP)
+                       continue;
+               if (item->command != TODO_PICK)
+                       break;
+               if (parse_commit(item->commit)) {
+                       todo_list_release(&todo_list);
+                       return error(_("could not parse commit '%s'"),
+                               oid_to_hex(&item->commit->object.oid));
+               }
+               if (!item->commit->parents)
+                       break; /* root commit */
+               if (item->commit->parents->next)
+                       break; /* merge commit */
+               parent_oid = &item->commit->parents->item->object.oid;
+               if (hashcmp(parent_oid->hash, oid->hash))
+                       break;
+               oid = &item->commit->object.oid;
+       }
+       if (i > 0) {
+               int offset = i < todo_list.nr ?
+                       todo_list.items[i].offset_in_buf : todo_list.buf.len;
+               const char *done_path = rebase_path_done();
+
+               fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
+               if (fd < 0) {
+                       error_errno(_("could not open '%s' for writing"),
+                                   done_path);
+                       todo_list_release(&todo_list);
+                       return -1;
+               }
+               if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
+                       error_errno(_("could not write to '%s'"), done_path);
+                       todo_list_release(&todo_list);
+                       close(fd);
+                       return -1;
+               }
+               close(fd);
+
+               fd = open(rebase_path_todo(), O_WRONLY, 0666);
+               if (fd < 0) {
+                       error_errno(_("could not open '%s' for writing"),
+                                   rebase_path_todo());
+                       todo_list_release(&todo_list);
+                       return -1;
+               }
+               if (write_in_full(fd, todo_list.buf.buf + offset,
+                               todo_list.buf.len - offset) < 0) {
+                       error_errno(_("could not write to '%s'"),
+                                   rebase_path_todo());
+                       close(fd);
+                       todo_list_release(&todo_list);
+                       return -1;
+               }
+               if (ftruncate(fd, todo_list.buf.len - offset) < 0) {
+                       error_errno(_("could not truncate '%s'"),
+                                   rebase_path_todo());
+                       todo_list_release(&todo_list);
+                       close(fd);
+                       return -1;
+               }
+               close(fd);
+
+               todo_list.current = i;
+               if (is_fixup(peek_command(&todo_list, 0)))
+                       record_in_rewritten(oid, peek_command(&todo_list, 0));
+       }
+
+       todo_list_release(&todo_list);
+       printf("%s\n", oid_to_hex(oid));
+
+       return 0;
+}
+
+struct subject2item_entry {
+       struct hashmap_entry entry;
+       int i;
+       char subject[FLEX_ARRAY];
+};
+
+static int subject2item_cmp(const void *fndata,
+                           const struct subject2item_entry *a,
+                           const struct subject2item_entry *b, const void *key)
+{
+       return key ? strcmp(a->subject, key) : strcmp(a->subject, b->subject);
+}
+
+/*
+ * Rearrange the todo list that has both "pick commit-id msg" and "pick
+ * commit-id fixup!/squash! msg" in it so that the latter is put immediately
+ * after the former, and change "pick" to "fixup"/"squash".
+ *
+ * Note that if the config has specified a custom instruction format, each log
+ * message will have to be retrieved from the commit (as the oneline in the
+ * script cannot be trusted) in order to normalize the autosquash arrangement.
+ */
+int rearrange_squash(void)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       struct hashmap subject2item;
+       int res = 0, rearranged = 0, *next, *tail, fd, i;
+       char **subjects;
+
+       fd = open(todo_file, O_RDONLY);
+       if (fd < 0)
+               return error_errno(_("could not open '%s'"), todo_file);
+       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
+               close(fd);
+               return error(_("could not read '%s'."), todo_file);
+       }
+       close(fd);
+       if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {
+               todo_list_release(&todo_list);
+               return -1;
+       }
+
+       /*
+        * The hashmap maps onelines to the respective todo list index.
+        *
+        * If any items need to be rearranged, the next[i] value will indicate
+        * which item was moved directly after the i'th.
+        *
+        * In that case, last[i] will indicate the index of the latest item to
+        * be moved to appear after the i'th.
+        */
+       hashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,
+                    NULL, todo_list.nr);
+       ALLOC_ARRAY(next, todo_list.nr);
+       ALLOC_ARRAY(tail, todo_list.nr);
+       ALLOC_ARRAY(subjects, todo_list.nr);
+       for (i = 0; i < todo_list.nr; i++) {
+               struct strbuf buf = STRBUF_INIT;
+               struct todo_item *item = todo_list.items + i;
+               const char *commit_buffer, *subject, *p;
+               size_t subject_len;
+               int i2 = -1;
+               struct subject2item_entry *entry;
+
+               next[i] = tail[i] = -1;
+               if (item->command >= TODO_EXEC) {
+                       subjects[i] = NULL;
+                       continue;
+               }
+
+               if (is_fixup(item->command)) {
+                       todo_list_release(&todo_list);
+                       return error(_("the script was already rearranged."));
+               }
+
+               item->commit->util = item;
+
+               parse_commit(item->commit);
+               commit_buffer = get_commit_buffer(item->commit, NULL);
+               find_commit_subject(commit_buffer, &subject);
+               format_subject(&buf, subject, " ");
+               subject = subjects[i] = strbuf_detach(&buf, &subject_len);
+               unuse_commit_buffer(item->commit, commit_buffer);
+               if ((skip_prefix(subject, "fixup! ", &p) ||
+                    skip_prefix(subject, "squash! ", &p))) {
+                       struct commit *commit2;
+
+                       for (;;) {
+                               while (isspace(*p))
+                                       p++;
+                               if (!skip_prefix(p, "fixup! ", &p) &&
+                                   !skip_prefix(p, "squash! ", &p))
+                                       break;
+                       }
+
+                       if ((entry = hashmap_get_from_hash(&subject2item,
+                                                          strhash(p), p)))
+                               /* found by title */
+                               i2 = entry->i;
+                       else if (!strchr(p, ' ') &&
+                                (commit2 =
+                                 lookup_commit_reference_by_name(p)) &&
+                                commit2->util)
+                               /* found by commit name */
+                               i2 = (struct todo_item *)commit2->util
+                                       - todo_list.items;
+                       else {
+                               /* copy can be a prefix of the commit subject */
+                               for (i2 = 0; i2 < i; i2++)
+                                       if (subjects[i2] &&
+                                           starts_with(subjects[i2], p))
+                                               break;
+                               if (i2 == i)
+                                       i2 = -1;
+                       }
+               }
+               if (i2 >= 0) {
+                       rearranged = 1;
+                       todo_list.items[i].command =
+                               starts_with(subject, "fixup!") ?
+                               TODO_FIXUP : TODO_SQUASH;
+                       if (next[i2] < 0)
+                               next[i2] = i;
+                       else
+                               next[tail[i2]] = i;
+                       tail[i2] = i;
+               } else if (!hashmap_get_from_hash(&subject2item,
+                                               strhash(subject), subject)) {
+                       FLEX_ALLOC_MEM(entry, subject, subject, subject_len);
+                       entry->i = i;
+                       hashmap_entry_init(entry, strhash(entry->subject));
+                       hashmap_put(&subject2item, entry);
+               }
+       }
+
+       if (rearranged) {
+               struct strbuf buf = STRBUF_INIT;
+
+               for (i = 0; i < todo_list.nr; i++) {
+                       enum todo_command command = todo_list.items[i].command;
+                       int cur = i;
+
+                       /*
+                        * Initially, all commands are 'pick's. If it is a
+                        * fixup or a squash now, we have rearranged it.
+                        */
+                       if (is_fixup(command))
+                               continue;
+
+                       while (cur >= 0) {
+                               int offset = todo_list.items[cur].offset_in_buf;
+                               int end_offset = cur + 1 < todo_list.nr ?
+                                       todo_list.items[cur + 1].offset_in_buf :
+                                       todo_list.buf.len;
+                               char *bol = todo_list.buf.buf + offset;
+                               char *eol = todo_list.buf.buf + end_offset;
+
+                               /* replace 'pick', by 'fixup' or 'squash' */
+                               command = todo_list.items[cur].command;
+                               if (is_fixup(command)) {
+                                       strbuf_addstr(&buf,
+                                               todo_command_info[command].str);
+                                       bol += strcspn(bol, " \t");
+                               }
+
+                               strbuf_add(&buf, bol, eol - bol);
+
+                               cur = next[cur];
+                       }
+               }
+
+               fd = open(todo_file, O_WRONLY);
+               if (fd < 0)
+                       res = error_errno(_("could not open '%s'"), todo_file);
+               else if (write(fd, buf.buf, buf.len) < 0)
+                       res = error_errno(_("could not read '%s'."), todo_file);
+               else if (ftruncate(fd, buf.len) < 0)
+                       res = error_errno(_("could not finish '%s'"),
+                                          todo_file);
+               close(fd);
+               strbuf_release(&buf);
+       }
+
+       free(next);
+       free(tail);
+       for (i = 0; i < todo_list.nr; i++)
+               free(subjects[i]);
+       free(subjects);
+       hashmap_free(&subject2item, 1);
+       todo_list_release(&todo_list);
+
+       return res;
+}
index f885b68395f4bff1ded96c0ab84ed87d164f0c7d..6f3d3df82c0ade64b7b125acd49bf3f5e15c53af 100644 (file)
@@ -45,6 +45,14 @@ int sequencer_continue(struct replay_opts *opts);
 int sequencer_rollback(struct replay_opts *opts);
 int sequencer_remove_state(struct replay_opts *opts);
 
+int sequencer_make_script(int keep_empty, FILE *out,
+               int argc, const char **argv);
+
+int transform_todo_ids(int shorten_ids);
+int check_todo_list(void);
+int skip_unnecessary_picks(void);
+int rearrange_squash(void);
+
 extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
diff --git a/setup.c b/setup.c
index 23950173fc01268320d2e23c36ef80a1b1231a5e..d777ff34b7daf50fb24a8653a7e26e3e95b8c0c7 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;
 }
@@ -546,7 +541,8 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir)
 
 /*
  * Try to read the location of the git directory from the .git file,
- * return path to git directory if found.
+ * return path to git directory if found. The return value comes from
+ * a shared buffer.
  *
  * On failure, if return_error_code is not NULL, return_error_code
  * will be set to an error code and NULL will be returned. If
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 f56bb5cae7ba5aed095bff90672b24c616cbcf54..09ad64ce555e0b908ce0d9f22219026a2d37ae0c 100644 (file)
@@ -30,7 +30,7 @@
 #include "quote.h"
 #include "packfile.h"
 
-const unsigned char null_sha1[20];
+const unsigned char null_sha1[GIT_MAX_RAWSZ];
 const struct object_id null_oid;
 const struct object_id empty_tree_oid = {
        EMPTY_TREE_SHA1_BIN_LITERAL
@@ -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);
 }
@@ -1757,10 +1748,15 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
                ret = index_mem(sha1, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
-               if (size == read_in_full(fd, buf, size))
-                       ret = index_mem(sha1, buf, size, type, path, flags);
+               ssize_t read_result = read_in_full(fd, buf, size);
+               if (read_result < 0)
+                       ret = error_errno("read error while indexing %s",
+                                         path ? path : "<unknown>");
+               else if (read_result != size)
+                       ret = error("short read while indexing %s",
+                                   path ? path : "<unknown>");
                else
-                       ret = error_errno("short read");
+                       ret = index_mem(sha1, buf, size, type, path, flags);
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
@@ -1820,6 +1816,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 +1833,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 +1841,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..df4d44ea7a34a6d6bef119d1494f1c055614cbb1 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -99,7 +99,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
                                cur_depth = 0;
                        } else {
                                commit = (struct commit *)
-                                       stack.objects[--stack.nr].item;
+                                       object_array_pop(&stack);
                                cur_depth = *(int *)commit->util;
                        }
                }
@@ -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);
diff --git a/shell.c b/shell.c
index fe2d314593ba5ee3b985b778c2e7ebfdcd9da44c..234b2d4f16fe79e9260c8409bd4d7c964fe27e72 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -25,19 +25,6 @@ static int do_generic_cmd(const char *me, char *arg)
        return execv_git_cmd(my_argv);
 }
 
-static int do_cvs_cmd(const char *me, char *arg)
-{
-       const char *cvsserver_argv[3] = {
-               "cvsserver", "server", NULL
-       };
-
-       if (!arg || strcmp(arg, "server"))
-               die("git-cvsserver only handles server: %s", arg);
-
-       setup_path();
-       return execv_git_cmd(cvsserver_argv);
-}
-
 static int is_valid_cmd_name(const char *cmd)
 {
        /* Test command contains no . or / characters */
@@ -134,7 +121,6 @@ static struct commands {
        { "git-receive-pack", do_generic_cmd },
        { "git-upload-pack", do_generic_cmd },
        { "git-upload-archive", do_generic_cmd },
-       { "cvs", do_cvs_cmd },
        { NULL },
 };
 
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..ff8f6094a3300d7bbdbb1a1ed58c8037daff5c7a 100644 (file)
@@ -1,6 +1,69 @@
 #ifndef STRING_LIST_H
 #define STRING_LIST_H
 
+/**
+ * The string_list API offers a data structure and functions to handle
+ * sorted and unsorted arrays of strings.  A "sorted" list is one whose
+ * entries are sorted by string value in `strcmp()` order.
+ *
+ * The caller:
+ *
+ * . Allocates and clears a `struct string_list` variable.
+ *
+ * . Initializes the members. You might want to set the flag `strdup_strings`
+ *   if the strings should be strdup()ed. For example, this is necessary
+ *   when you add something like git_path("..."), since that function returns
+ *   a static buffer that will change with the next call to git_path().
+ *
+ * If you need something advanced, you can manually malloc() the `items`
+ * member (you need this if you add things later) and you should set the
+ * `nr` and `alloc` members in that case, too.
+ *
+ * . Adds new items to the list, using `string_list_append`,
+ *   `string_list_append_nodup`, `string_list_insert`,
+ *   `string_list_split`, and/or `string_list_split_in_place`.
+ *
+ * . Can check if a string is in the list using `string_list_has_string` or
+ *   `unsorted_string_list_has_string` and get it from the list using
+ *   `string_list_lookup` for sorted lists.
+ *
+ * . Can sort an unsorted list using `string_list_sort`.
+ *
+ * . Can remove duplicate items from a sorted list using
+ *   `string_list_remove_duplicates`.
+ *
+ * . Can remove individual items of an unsorted list using
+ *   `unsorted_string_list_delete_item`.
+ *
+ * . Can remove items not matching a criterion from a sorted or unsorted
+ *   list using `filter_string_list`, or remove empty strings using
+ *   `string_list_remove_empty_items`.
+ *
+ * . Finally it should free the list using `string_list_clear`.
+ *
+ * Example:
+ *
+ *     struct string_list list = STRING_LIST_INIT_NODUP;
+ *     int i;
+ *
+ *     string_list_append(&list, "foo");
+ *     string_list_append(&list, "bar");
+ *     for (i = 0; i < list.nr; i++)
+ *             printf("%s\n", list.items[i].string)
+ *
+ * NOTE: It is more efficient to build an unsorted list and sort it
+ * afterwards, instead of building a sorted list (`O(n log n)` instead of
+ * `O(n^2)`).
+ *
+ * However, if you use the list to check if a certain string was added
+ * already, you should not do that (using unsorted_string_list_has_string()),
+ * because the complexity would be quadratic again (but with a worse factor).
+ */
+
+/**
+ * Represents an item of the list. The `string` member is a pointer to the
+ * string, and you may use the `util` member for any purpose, if you want.
+ */
 struct string_list_item {
        char *string;
        void *util;
@@ -8,6 +71,18 @@ struct string_list_item {
 
 typedef int (*compare_strings_fn)(const char *, const char *);
 
+/**
+ * Represents the list itself.
+ *
+ * . The array of items are available via the `items` member.
+ * . The `nr` member contains the number of items stored in the list.
+ * . The `alloc` member is used to avoid reallocating at every insertion.
+ *   You should not tamper with it.
+ * . Setting the `strdup_strings` member to 1 will strdup() the strings
+ *   before adding them, see above.
+ * . The `compare_strings_fn` member is used to specify a custom compare
+ *   function, otherwise `strcmp()` is used as the default function.
+ */
 struct string_list {
        struct string_list_item *items;
        unsigned int nr, alloc;
@@ -18,33 +93,65 @@ struct string_list {
 #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0, NULL }
 #define STRING_LIST_INIT_DUP   { NULL, 0, 0, 1, NULL }
 
+/* General functions which work with both sorted and unsorted lists. */
+
+/**
+ * Initialize the members of the string_list, set `strdup_strings`
+ * member according to the value of the second parameter.
+ */
 void string_list_init(struct string_list *list, int strdup_strings);
 
+/** Callback function type for for_each_string_list */
+typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
+
+/**
+ * Apply `want` to each item in `list`, retaining only the ones for which
+ * the function returns true.  If `free_util` is true, call free() on
+ * the util members of any items that have to be deleted.  Preserve
+ * the order of the items that are retained.
+ */
+void filter_string_list(struct string_list *list, int free_util,
+                       string_list_each_func_t want, void *cb_data);
+
+/**
+ * Dump a string_list to stdout, useful mainly for debugging
+ * purposes. It can take an optional header argument and it writes out
+ * the string-pointer pairs of the string_list, each one in its own
+ * line.
+ */
 void print_string_list(const struct string_list *p, const char *text);
+
+/**
+ * Free a string_list. The `string` pointer of the items will be freed
+ * in case the `strdup_strings` member of the string_list is set. The
+ * second parameter controls if the `util` pointer of the items should
+ * be freed or not.
+ */
 void string_list_clear(struct string_list *list, int free_util);
 
-/* Use this function to call a custom clear function on each util pointer */
-/* The string associated with the util pointer is passed as the second argument */
+/**
+ * Callback type for `string_list_clear_func`.  The string associated
+ * with the util pointer is passed as the second argument
+ */
 typedef void (*string_list_clear_func_t)(void *p, const char *str);
+
+/** Call a custom clear function on each util pointer */
 void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
 
-/* Use this function or the macro below to iterate over each item */
-typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
+/**
+ * Apply `func` to each item. If `func` returns nonzero, the
+ * iteration aborts and the return value is propagated.
+ */
 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)
+                        string_list_each_func_t func, void *cb_data);
 
-/*
- * Apply want to each item in list, retaining only the ones for which
- * the function returns true.  If free_util is true, call free() on
- * the util members of any items that have to be deleted.  Preserve
- * the order of the items that are retained.
- */
-void filter_string_list(struct string_list *list, int free_util,
-                       string_list_each_func_t want, void *cb_data);
+/** Iterate over each item, as a macro. */
+#define for_each_string_list_item(item,list)            \
+       for (item = (list)->items;                      \
+            item && item < (list)->items + (list)->nr; \
+            ++item)
 
-/*
+/**
  * Remove any empty strings from the list.  If free_util is true, call
  * free() on the util members of any items that have to be deleted.
  * Preserve the order of the items that are retained.
@@ -52,25 +159,34 @@ void filter_string_list(struct string_list *list, int free_util,
 void string_list_remove_empty_items(struct string_list *list, int free_util);
 
 /* Use these functions only on sorted lists: */
+
+/** Determine if the string_list has a given string or not. */
 int string_list_has_string(const struct string_list *list, const char *string);
 int string_list_find_insert_index(const struct string_list *list, const char *string,
                                  int negative_existing_index);
-/*
- * Inserts the given string into the sorted list.
- * If the string already exists, the list is not altered.
- * Returns the string_list_item, the string is part of.
+
+/**
+ * Insert a new element to the string_list. The returned pointer can
+ * be handy if you want to write something to the `util` pointer of
+ * the string_list_item containing the just added string. If the given
+ * string already exists the insertion will be skipped and the pointer
+ * to the existing item returned.
+ *
+ * Since this function uses xrealloc() (which die()s if it fails) if the
+ * list needs to grow, it is safe not to check the pointer. I.e. you may
+ * write `string_list_insert(...)->util = ...;`.
  */
 struct string_list_item *string_list_insert(struct string_list *list, const char *string);
 
-/*
- * Removes the given string from the sorted list.
- * If the string doesn't exist, the list is not altered.
+/**
+ * Remove the given string from the sorted list.  If the string
+ * doesn't exist, the list is not altered.
  */
 extern void string_list_remove(struct string_list *list, const char *string,
                               int free_util);
 
-/*
- * Checks if the given string is part of a sorted list. If it is part of the list,
+/**
+ * Check if the given string is part of a sorted list. If it is part of the list,
  * return the coresponding string_list_item, NULL otherwise.
  */
 struct string_list_item *string_list_lookup(struct string_list *list, const char *string);
@@ -85,14 +201,14 @@ void string_list_remove_duplicates(struct string_list *sorted_list, int free_uti
 
 /* Use these functions only on unsorted lists: */
 
-/*
+/**
  * Add string to the end of list.  If list->strdup_string is set, then
  * string is copied; otherwise the new string_list_entry refers to the
  * input string.
  */
 struct string_list_item *string_list_append(struct string_list *list, const char *string);
 
-/*
+/**
  * Like string_list_append(), except string is never copied.  When
  * list->strdup_strings is set, this function can be used to hand
  * ownership of a malloc()ed string to list without making an extra
@@ -100,16 +216,34 @@ struct string_list_item *string_list_append(struct string_list *list, const char
  */
 struct string_list_item *string_list_append_nodup(struct string_list *list, char *string);
 
+/**
+ * Sort the list's entries by string value in `strcmp()` order.
+ */
 void string_list_sort(struct string_list *list);
+
+/**
+ * Like `string_list_has_string()` but for unsorted lists. Linear in
+ * size of the list.
+ */
 int unsorted_string_list_has_string(struct string_list *list, const char *string);
+
+/**
+ * Like `string_list_lookup()` but for unsorted lists. Linear in size
+ * of the list.
+ */
 struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
                                                     const char *string);
-
+/**
+ * Remove an item from a string_list. The `string` pointer of the
+ * items will be freed in case the `strdup_strings` member of the
+ * string_list is set. The third parameter controls if the `util`
+ * pointer of the items should be freed or not.
+ */
 void unsorted_string_list_delete_item(struct string_list *list, int i, int free_util);
 
-/*
- * Split string into substrings on character delim and append the
- * substrings to list.  The input string is not modified.
+/**
+ * Split string into substrings on character `delim` and append the
+ * substrings to `list`.  The input string is not modified.
  * list->strdup_strings must be set, as new memory needs to be
  * allocated to hold the substrings.  If maxsplit is non-negative,
  * then split at most maxsplit times.  Return the number of substrings
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..63e7094e1620c2c3eb7dda0a8c38a833ae2d1c5a 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
@@ -496,7 +503,7 @@ static void show_submodule_header(struct diff_options *o, const char *path,
 
        if (add_submodule_odb(path)) {
                if (!message)
-                       message = "(not initialized)";
+                       message = "(commits not present)";
                goto output_header;
        }
 
@@ -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 */
@@ -1659,7 +1685,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                        add_object_array(merges.objects[i].item, NULL, result);
        }
 
-       free(merges.objects);
+       object_array_clear(&merges);
        return result->nr;
 }
 
@@ -1764,7 +1790,7 @@ int merge_submodule(struct object_id *result, const char *path,
                        print_commit((struct commit *) merges.objects[i].item);
        }
 
-       free(merges.objects);
+       object_array_clear(&merges);
        return 0;
 }
 
@@ -1971,6 +1997,10 @@ const char *get_superproject_working_tree(void)
        return ret;
 }
 
+/*
+ * Put the gitdir for a submodule (given relative to the main
+ * repository worktree) into `buf`, or return -1 on error.
+ */
 int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
 {
        const struct submodule *sub;
index 6b52133c88b2306e77fe6f759507b38c0ec0d6ce..f0da0277a45b49369d89cebdd4d30f44bef23d3d 100644 (file)
@@ -120,7 +120,7 @@ extern int submodule_move_head(const char *path,
 
 /*
  * Prepare the "env_array" parameter of a "struct child_process" for executing
- * a submodule by clearing any repo-specific envirionment variables, but
+ * a submodule by clearing any repo-specific environment variables, but
  * retaining any config in the environment.
  */
 extern void prepare_submodule_repo_env(struct argv_array *out);
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 81575fe2ab91b550067ce8180650b003bdd939b7..078dd7e29d0844f217a3a76144df312879c8536c 100644 (file)
@@ -17,27 +17,17 @@ static uint32_t strtouint32(const char *s)
 
 static void handle_command(const char *command, const char *arg, struct line_buffer *buf)
 {
-       switch (*command) {
-       case 'b':
-               if (starts_with(command, "binary ")) {
-                       struct strbuf sb = STRBUF_INIT;
-                       strbuf_addch(&sb, '>');
-                       buffer_read_binary(buf, &sb, strtouint32(arg));
-                       fwrite(sb.buf, 1, sb.len, stdout);
-                       strbuf_release(&sb);
-                       return;
-               }
-       case 'c':
-               if (starts_with(command, "copy ")) {
-                       buffer_copy_bytes(buf, strtouint32(arg));
-                       return;
-               }
-       case 's':
-               if (starts_with(command, "skip ")) {
-                       buffer_skip_bytes(buf, strtouint32(arg));
-                       return;
-               }
-       default:
+       if (starts_with(command, "binary ")) {
+               struct strbuf sb = STRBUF_INIT;
+               strbuf_addch(&sb, '>');
+               buffer_read_binary(buf, &sb, strtouint32(arg));
+               fwrite(sb.buf, 1, sb.len, stdout);
+               strbuf_release(&sb);
+       } else if (starts_with(command, "copy ")) {
+               buffer_copy_bytes(buf, strtouint32(arg));
+       } else if (starts_with(command, "skip ")) {
+               buffer_skip_bytes(buf, strtouint32(arg));
+       } else {
                die("unrecognized command: %s", command);
        }
 }
index 75fe883aac1ee17b9dda40dd88b7a4a4fd328318..630c76d1275485d2d460896407e645116c997916 100644 (file)
@@ -99,6 +99,8 @@ int cmd_main(int argc, const char **argv)
        const char *prefix = "prefix/";
        const char *usage[] = {
                "test-parse-options <options>",
+               "",
+               "A helper function for the parse-options API.",
                NULL
        };
        struct string_list expect = STRING_LIST_INIT_NODUP;
index 74d2cd76fe56bf6d6fab5d44834999b55d3220de..0c2fc81d7b0fa401db58c41cd2fed4469e80b058 100755 (executable)
@@ -10,6 +10,8 @@ test_description='our own option parser'
 cat >expect <<\EOF
 usage: test-parse-options <options>
 
+    A helper function for the parse-options API.
+
     --yes                 get a boolean
     -D, --no-doubt        begins with 'no-'
     -B, --no-fear         be brave
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 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..a859abedf5820da7fd2a2457c6d19b213ff78784 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
@@ -35,6 +38,25 @@ test_expect_success 'setup optionspec' '
 EOF
 '
 
+test_expect_success 'setup optionspec-no-switches' '
+       sed -e "s/^|//" >optionspec_no_switches <<\EOF
+|some-command [options] <args>...
+|
+|some-command does foo and bar!
+|--
+EOF
+'
+
+test_expect_success 'setup optionspec-only-hidden-switches' '
+       sed -e "s/^|//" >optionspec_only_hidden_switches <<\EOF
+|some-command [options] <args>...
+|
+|some-command does foo and bar!
+|--
+|hidden1* A hidden switch
+EOF
+'
+
 test_expect_success 'test --parseopt help output' '
        sed -e "s/^|//" >expect <<\END_EXPECT &&
 |cat <<\EOF
@@ -62,6 +84,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
@@ -73,19 +98,100 @@ END_EXPECT
        test_i18ncmp expect output
 '
 
+test_expect_success 'test --parseopt help output no switches' '
+       sed -e "s/^|//" >expect <<\END_EXPECT &&
+|cat <<\EOF
+|usage: some-command [options] <args>...
+|
+|    some-command does foo and bar!
+|
+|EOF
+END_EXPECT
+       test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_no_switches &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'test --parseopt help output hidden switches' '
+       sed -e "s/^|//" >expect <<\END_EXPECT &&
+|cat <<\EOF
+|usage: some-command [options] <args>...
+|
+|    some-command does foo and bar!
+|
+|EOF
+END_EXPECT
+       test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'test --parseopt help-all output hidden switches' '
+       sed -e "s/^|//" >expect <<\END_EXPECT &&
+|cat <<\EOF
+|usage: some-command [options] <args>...
+|
+|    some-command does foo and bar!
+|
+|    --hidden1             A hidden switch
+|
+|EOF
+END_EXPECT
+       test_expect_code 129 git rev-parse --parseopt -- --help-all > output < optionspec_only_hidden_switches &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'test --parseopt invalid switch help output' '
+       sed -e "s/^|//" >expect <<\END_EXPECT &&
+|error: unknown option `does-not-exist'\''
+|usage: some-command [options] <args>...
+|
+|    some-command does foo and bar!
+|
+|    -h, --help            show the help
+|    --foo                 some nifty option --foo
+|    --bar ...             some cool option --bar with an argument
+|    -b, --baz             a short and long option
+|
+|An option group Header
+|    -C[...]               option C with an optional argument
+|    -d, --data[=...]      short and long option with an optional argument
+|
+|Argument hints
+|    -B <arg>              short option required argument
+|    --bar2 <arg>          long option required argument
+|    -e, --fuz <with-space>
+|                          short and long option required argument
+|    -s[<some>]            short option optional argument
+|    --long[=<data>]       long option optional argument
+|    -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
+|    --extra1              line above used to cause a segfault but no longer does
+|
+END_EXPECT
+       test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&
+       test_i18ncmp expect output
+'
+
 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 9d707d2a40a457abcd85fb93d9f8195946d09d01..3ac7ebf85f3198cde56f784456e0024205aec853 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 &&
@@ -368,6 +381,262 @@ test_expect_success 'config information was renamed, too' '
        test_must_fail git config branch.s/s.dummy
 '
 
+test_expect_success 'git branch -m correctly renames multiple config sections' '
+       test_when_finished "git checkout master" &&
+       git checkout -b source master &&
+
+       # Assert that a config file with multiple config sections has
+       # those sections preserved...
+       cat >expect <<-\EOF &&
+       branch.dest.key1=value1
+       some.gar.b=age
+       branch.dest.key2=value2
+       EOF
+       cat >config.branch <<\EOF &&
+;; Note the lack of -\EOF above & mixed indenting here. This is
+;; intentional, we are also testing that the formatting of copied
+;; sections is preserved.
+
+;; Comment for source. Tabs
+[branch "source"]
+       ;; Comment for the source value
+       key1 = value1
+;; Comment for some.gar. Spaces
+[some "gar"]
+    ;; Comment for the some.gar value
+    b = age
+;; Comment for source, again. Mixed tabs/spaces.
+[branch "source"]
+    ;; Comment for the source value, again
+       key2 = value2
+EOF
+       cat config.branch >>.git/config &&
+       git branch -m source dest &&
+       git config -f .git/config -l | grep -F -e source -e dest -e some.gar >actual &&
+       test_cmp expect actual &&
+
+       # ...and that the comments for those sections are also
+       # preserved.
+       cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
+       sed -n -e "/Note the lack/,\$p" .git/config >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git branch -c dumps usage' '
+       test_expect_code 128 git branch -c 2>err &&
+       test_i18ngrep "branch name required" err
+'
+
+test_expect_success 'git branch --copy dumps usage' '
+       test_expect_code 128 git branch --copy 2>err &&
+       test_i18ngrep "branch name required" err
+'
+
+test_expect_success 'git branch -c d e should work' '
+       git branch -l d &&
+       git reflog exists refs/heads/d &&
+       git config branch.d.dummy Hello &&
+       git branch -c d e &&
+       git reflog exists refs/heads/d &&
+       git reflog exists refs/heads/e &&
+       echo Hello >expect &&
+       git config branch.e.dummy >actual &&
+       test_cmp expect actual &&
+       echo Hello >expect &&
+       git config branch.d.dummy >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git branch --copy is a synonym for -c' '
+       git branch -l copy &&
+       git reflog exists refs/heads/copy &&
+       git config branch.copy.dummy Hello &&
+       git branch --copy copy copy-to &&
+       git reflog exists refs/heads/copy &&
+       git reflog exists refs/heads/copy-to &&
+       echo Hello >expect &&
+       git config branch.copy.dummy >actual &&
+       test_cmp expect actual &&
+       echo Hello >expect &&
+       git config branch.copy-to.dummy >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git branch -c ee ef should copy ee to create branch ef' '
+       git checkout -b ee &&
+       git reflog exists refs/heads/ee &&
+       git config branch.ee.dummy Hello &&
+       git branch -c ee ef &&
+       git reflog exists refs/heads/ee &&
+       git reflog exists refs/heads/ef &&
+       test $(git config branch.ee.dummy) = Hello &&
+       test $(git config branch.ef.dummy) = Hello &&
+       test $(git rev-parse --abbrev-ref HEAD) = ee
+'
+
+test_expect_success 'git branch -c f/f g/g should work' '
+       git branch -l f/f &&
+       git reflog exists refs/heads/f/f &&
+       git config branch.f/f.dummy Hello &&
+       git branch -c f/f g/g &&
+       git reflog exists refs/heads/f/f &&
+       git reflog exists refs/heads/g/g &&
+       test $(git config branch.f/f.dummy) = Hello &&
+       test $(git config branch.g/g.dummy) = Hello
+'
+
+test_expect_success 'git branch -c m2 m2 should work' '
+       git branch -l m2 &&
+       git reflog exists refs/heads/m2 &&
+       git config branch.m2.dummy Hello &&
+       git branch -c m2 m2 &&
+       git reflog exists refs/heads/m2 &&
+       test $(git config branch.m2.dummy) = Hello
+'
+
+test_expect_success 'git branch -c zz zz/zz should fail' '
+       git branch -l zz &&
+       git reflog exists refs/heads/zz &&
+       test_must_fail git branch -c zz zz/zz
+'
+
+test_expect_success 'git branch -c b/b b should fail' '
+       git branch -l b/b &&
+       test_must_fail git branch -c b/b b
+'
+
+test_expect_success 'git branch -C o/q o/p should work when o/p exists' '
+       git branch -l o/q &&
+       git reflog exists refs/heads/o/q &&
+       git reflog exists refs/heads/o/p &&
+       git branch -C o/q o/p
+'
+
+test_expect_success 'git branch -c -f o/q o/p should work when o/p exists' '
+       git reflog exists refs/heads/o/q &&
+       git reflog exists refs/heads/o/p &&
+       git branch -c -f o/q o/p
+'
+
+test_expect_success 'git branch -c qq rr/qq should fail when r exists' '
+       git branch qq &&
+       git branch rr &&
+       test_must_fail git branch -c qq rr/qq
+'
+
+test_expect_success 'git branch -C b1 b2 should fail when b2 is checked out' '
+       git branch b1 &&
+       git checkout -b b2 &&
+       test_must_fail git branch -C b1 b2
+'
+
+test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out' '
+       git checkout -b c1 &&
+       git branch c2 &&
+       git branch -C c1 c2 &&
+       test $(git rev-parse --abbrev-ref HEAD) = c1
+'
+
+test_expect_success 'git branch -C c1 c2 should never touch HEAD' '
+       msg="Branch: copied refs/heads/c1 to refs/heads/c2" &&
+       ! grep "$msg$" .git/logs/HEAD
+'
+
+test_expect_success 'git branch -C master should work when master is checked out' '
+       git checkout master &&
+       git branch -C master
+'
+
+test_expect_success 'git branch -C master master should work when master is checked out' '
+       git checkout master &&
+       git branch -C master master
+'
+
+test_expect_success 'git branch -C master5 master5 should work when master is checked out' '
+       git checkout master &&
+       git branch master5 &&
+       git branch -C master5 master5
+'
+
+test_expect_success 'git branch -C ab cd should overwrite existing config for cd' '
+       git branch -l cd &&
+       git reflog exists refs/heads/cd &&
+       git config branch.cd.dummy CD &&
+       git branch -l ab &&
+       git reflog exists refs/heads/ab &&
+       git config branch.ab.dummy AB &&
+       git branch -C ab cd &&
+       git reflog exists refs/heads/ab &&
+       git reflog exists refs/heads/cd &&
+       test $(git config branch.ab.dummy) = AB &&
+       test $(git config branch.cd.dummy) = AB
+'
+
+test_expect_success 'git branch -c correctly copies multiple config sections' '
+       FOO=1 &&
+       export FOO &&
+       test_when_finished "git checkout master" &&
+       git checkout -b source2 master &&
+
+       # Assert that a config file with multiple config sections has
+       # those sections preserved...
+       cat >expect <<-\EOF &&
+       branch.source2.key1=value1
+       branch.dest2.key1=value1
+       more.gar.b=age
+       branch.source2.key2=value2
+       branch.dest2.key2=value2
+       EOF
+       cat >config.branch <<\EOF &&
+;; Note the lack of -\EOF above & mixed indenting here. This is
+;; intentional, we are also testing that the formatting of copied
+;; sections is preserved.
+
+;; Comment for source2. Tabs
+[branch "source2"]
+       ;; Comment for the source2 value
+       key1 = value1
+;; Comment for more.gar. Spaces
+[more "gar"]
+    ;; Comment for the more.gar value
+    b = age
+;; Comment for source2, again. Mixed tabs/spaces.
+[branch "source2"]
+    ;; Comment for the source2 value, again
+       key2 = value2
+EOF
+       cat config.branch >>.git/config &&
+       git branch -c source2 dest2 &&
+       git config -f .git/config -l | grep -F -e source2 -e dest2 -e more.gar >actual &&
+       test_cmp expect actual &&
+
+       # ...and that the comments and formatting for those sections
+       # is also preserved.
+       cat >expect <<\EOF &&
+;; Comment for source2. Tabs
+[branch "source2"]
+       ;; Comment for the source2 value
+       key1 = value1
+;; Comment for more.gar. Spaces
+[branch "dest2"]
+       ;; Comment for the source2 value
+       key1 = value1
+;; Comment for more.gar. Spaces
+[more "gar"]
+    ;; Comment for the more.gar value
+    b = age
+;; Comment for source2, again. Mixed tabs/spaces.
+[branch "source2"]
+    ;; Comment for the source2 value, again
+       key2 = value2
+[branch "dest2"]
+    ;; Comment for the source2 value, again
+       key2 = value2
+EOF
+       sed -n -e "/Comment for source2/,\$p" .git/config >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'deleting a symref' '
        git branch target &&
        git symbolic-ref refs/heads/symref refs/heads/target &&
@@ -559,6 +828,7 @@ test_expect_success 'use --set-upstream-to modify HEAD' '
 test_expect_success 'use --set-upstream-to modify a particular branch' '
        git branch my13 &&
        git branch --set-upstream-to master my13 &&
+       test_when_finished "git branch --unset-upstream my13" &&
        test "$(git config branch.my13.remote)" = "." &&
        test "$(git config branch.my13.merge)" = "refs/heads/master"
 '
@@ -604,38 +874,8 @@ test_expect_success 'test --unset-upstream on a particular branch' '
        test_must_fail git config branch.my14.merge
 '
 
-test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' '
-       git update-ref refs/remotes/origin/master HEAD &&
-       git branch --set-upstream origin/master 2>actual &&
-       test_when_finished git update-ref -d refs/remotes/origin/master &&
-       test_when_finished git branch -d origin/master &&
-       cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-
-If you wanted to make '"'master'"' track '"'origin/master'"', do this:
-
-    git branch -d origin/master
-    git branch --set-upstream-to origin/master
-EOF
-       test_i18ncmp expected actual
-'
-
-test_expect_success '--set-upstream with two args only shows the deprecation message' '
-       git branch --set-upstream master my13 2>actual &&
-       test_when_finished git branch --unset-upstream master &&
-       cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-EOF
-       test_i18ncmp expected actual
-'
-
-test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' '
-       git branch --set-upstream my13 2>actual &&
-       test_when_finished git branch --unset-upstream my13 &&
-       cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-EOF
-       test_i18ncmp expected actual
+test_expect_success '--set-upstream fails' '
+    test_must_fail git branch --set-upstream origin/master
 '
 
 test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '
@@ -960,19 +1200,6 @@ test_expect_success 'attempt to delete a branch merged to its base' '
        test_must_fail git branch -d my10
 '
 
-test_expect_success 'use set-upstream on the current branch' '
-       git checkout master &&
-       git --bare init myupstream.git &&
-       git push myupstream.git master:refs/heads/frotz &&
-       git remote add origin myupstream.git &&
-       git fetch &&
-       git branch --set-upstream master origin/frotz &&
-
-       test "z$(git config branch.master.remote)" = "zorigin" &&
-       test "z$(git config branch.master.merge)" = "zrefs/heads/frotz"
-
-'
-
 test_expect_success 'use --edit-description' '
        write_script editor <<-\EOF &&
                echo "New contents" >"$1"
index 37821d245433f757fa13f0a3e27da0312bebb7db..3704dbb2ecf6046e09adb03b451e4e531509b2f4 100755 (executable)
@@ -1249,20 +1249,13 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
        test B = $(git cat-file commit HEAD^ | sed -ne \$p)
 '
 
-cat >expect <<EOF
-Warning: the command isn't recognized in the following line:
- - badcmd $(git rev-list --oneline -1 master~1)
-
-You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
-Or you can abort the rebase with 'git rebase --abort'.
-EOF
-
 test_expect_success 'static check of bad command' '
        rebase_setup_and_clean bad-cmd &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
                git rebase -i --root 2>actual &&
-       test_i18ncmp expect actual &&
+       test_i18ngrep "badcmd $(git rev-list --oneline -1 master~1)" actual &&
+       test_i18ngrep "You can fix this with .git rebase --edit-todo.." actual &&
        FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo &&
        git rebase --continue &&
        test E = $(git cat-file commit HEAD | sed -ne \$p) &&
@@ -1284,20 +1277,13 @@ test_expect_success 'tabs and spaces are accepted in the todolist' '
        test E = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
-cat >expect <<EOF
-Warning: the SHA-1 is missing or isn't a commit in the following line:
- - edit XXXXXXX False commit
-
-You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
-Or you can abort the rebase with 'git rebase --abort'.
-EOF
-
 test_expect_success 'static check of bad SHA-1' '
        rebase_setup_and_clean bad-sha &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
                git rebase -i --root 2>actual &&
-       test_i18ncmp expect actual &&
+       test_i18ngrep "edit XXXXXXX False commit" actual &&
+       test_i18ngrep "You can fix this with .git rebase --edit-todo.." actual &&
        FAKE_LINES="1 2 4 5 6" git rebase --edit-todo &&
        git rebase --continue &&
        test E = $(git cat-file commit HEAD | sed -ne \$p)
index 5848949ec3700c2188d581500c19eaffa1392c3d..e364c12622f8566c3c99370de25ec5659a1e1546 100755 (executable)
@@ -271,6 +271,18 @@ test_expect_success C_LOCALE_OUTPUT 'autosquash with custom inst format' '
        test 2 = $(git cat-file commit HEAD^ | grep squash | wc -l)
 '
 
+test_expect_success 'autosquash with empty custom instructionFormat' '
+       git reset --hard base &&
+       test_commit empty-instructionFormat-test &&
+       (
+               set_cat_todo_editor &&
+               test_must_fail git -c rebase.instructionFormat= \
+                       rebase --autosquash  --force -i HEAD^ >actual &&
+               git log -1 --format="pick %h %s" >expect &&
+               test_cmp expect actual
+       )
+'
+
 set_backup_editor () {
        write_script backup-editor.sh <<-\EOF
        cp "$1" .git/backup-"$(basename "$1")"
@@ -278,7 +290,7 @@ set_backup_editor () {
        test_set_editor "$PWD/backup-editor.sh"
 }
 
-test_expect_failure 'autosquash with multiple empty patches' '
+test_expect_success 'autosquash with multiple empty patches' '
        test_tick &&
        git commit --allow-empty -m "empty" &&
        test_tick &&
@@ -304,4 +316,18 @@ test_expect_success 'extra spaces after fixup!' '
        test $base = $parent
 '
 
+test_expect_success 'wrapped original subject' '
+       if test -d .git/rebase-merge; then git rebase --abort; fi &&
+       base=$(git rev-parse HEAD) &&
+       echo "wrapped subject" >wrapped &&
+       git add wrapped &&
+       test_tick &&
+       git commit --allow-empty -m "$(printf "To\nfixup")" &&
+       test_tick &&
+       git commit --allow-empty -m "fixup! To fixup" &&
+       git rebase -i --autosquash --keep-empty HEAD~2 &&
+       parent=$(git rev-parse HEAD^) &&
+       test $base = $parent
+'
+
 test_done
index d09acfe48e81b2567fd7408d7cb6345c73d99754..c515e3e53feef29fd6c7434e13d52b3ea913e167 100755 (executable)
@@ -90,6 +90,14 @@ test_expect_success setup '
        git commit -m "Rearranged lines in dir/sub" &&
        git checkout master &&
 
+       GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" &&
+       GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
+       export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+       git checkout -b mode initial &&
+       git update-index --chmod=+x file0 &&
+       git commit -m "update mode" &&
+       git checkout -f master &&
+
        git config diff.renames false &&
 
        git show-branch
@@ -192,6 +200,10 @@ diff-tree --pretty side
 diff-tree --pretty -p side
 diff-tree --pretty --patch-with-stat side
 
+diff-tree initial mode
+diff-tree --stat initial mode
+diff-tree --summary initial mode
+
 diff-tree master
 diff-tree -p master
 diff-tree -p -m master
diff --git a/t/t4013/diff.diff-tree_--stat_initial_mode b/t/t4013/diff.diff-tree_--stat_initial_mode
new file mode 100644 (file)
index 0000000..0e5943c
--- /dev/null
@@ -0,0 +1,4 @@
+$ git diff-tree --stat initial mode
+ file0 | 0
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_--summary_initial_mode b/t/t4013/diff.diff-tree_--summary_initial_mode
new file mode 100644 (file)
index 0000000..25846b6
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff-tree --summary initial mode
+ mode change 100644 => 100755 file0
+$
diff --git a/t/t4013/diff.diff-tree_initial_mode b/t/t4013/diff.diff-tree_initial_mode
new file mode 100644 (file)
index 0000000..c47c094
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff-tree initial mode
+:100644 100755 01e79c32a8c99c557f0757da7cb6d65b3414466d 01e79c32a8c99c557f0757da7cb6d65b3414466d M     file0
+$
index b345b2ebfa53e51cb0ab32635a9123abdf55f2b1..2afe91f1167b14329108d438fa9cf9ed7963acad 100644 (file)
@@ -1,4 +1,10 @@
 $ git log --decorate=full --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (refs/heads/mode)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:06:00 2006 +0000
+
+    update mode
+
 commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:06:00 2006 +0000
index 3aa16a9e423bae96815718d0aef235060659a7c1..d0f308ab2bb73048cf153e209d7046e47682a524 100644 (file)
@@ -1,4 +1,10 @@
 $ git log --decorate --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (mode)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:06:00 2006 +0000
+
+    update mode
+
 commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:06:00 2006 +0000
index cd70fd5192ea88b9ccba4680f578d8c01be64945..49bca7b48d910fa1128435f121c849e45a7eefa6 100755 (executable)
@@ -95,7 +95,7 @@ test_expect_success 'submodule not initialized in new clone' '
        git clone . sm3 &&
        git -C sm3 diff-tree -p --no-commit-id --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
-       Submodule sm1 $smhead1...$smhead2 (not initialized)
+       Submodule sm1 $smhead1...$smhead2 (commits not present)
        EOF
        test_cmp expected actual
 '
index b04d955bfa8229a9c864ad53d1f9f1ec06260637..e9aa97117ab41beab708f4ded34628b015021e4e 100755 (executable)
@@ -7,11 +7,15 @@ test_description='git archive attribute tests'
 SUBSTFORMAT='%H (%h)%n'
 
 test_expect_exists() {
-       test_expect_success " $1 exists" "test -e $1"
+       test_expect_${2:-success} " $1 exists" "test -e $1"
 }
 
 test_expect_missing() {
-       test_expect_success " $1 does not exist" "test ! -e $1"
+       test_expect_${2:-success} " $1 does not exist" "test ! -e $1"
+}
+
+extract_tar_to_dir () {
+       (mkdir "$1" && cd "$1" && "$TAR" xf -) <"$1.tar"
 }
 
 test_expect_success 'setup' '
@@ -21,12 +25,19 @@ test_expect_success 'setup' '
 
        echo ignored by tree >ignored-by-tree &&
        echo ignored-by-tree export-ignore >.gitattributes &&
-       git add ignored-by-tree .gitattributes &&
+       mkdir ignored-by-tree.d &&
+       >ignored-by-tree.d/file &&
+       echo ignored-by-tree.d export-ignore >>.gitattributes &&
+       git add ignored-by-tree ignored-by-tree.d .gitattributes &&
 
        echo ignored by worktree >ignored-by-worktree &&
        echo ignored-by-worktree export-ignore >.gitattributes &&
        git add ignored-by-worktree &&
 
+       mkdir excluded-by-pathspec.d &&
+       >excluded-by-pathspec.d/file &&
+       git add excluded-by-pathspec.d &&
+
        printf "A\$Format:%s\$O" "$SUBSTFORMAT" >nosubstfile &&
        printf "A\$Format:%s\$O" "$SUBSTFORMAT" >substfile1 &&
        printf "A not substituted O" >substfile2 &&
@@ -46,7 +57,37 @@ test_expect_success 'git archive' '
 
 test_expect_missing    archive/ignored
 test_expect_missing    archive/ignored-by-tree
+test_expect_missing    archive/ignored-by-tree.d
+test_expect_missing    archive/ignored-by-tree.d/file
 test_expect_exists     archive/ignored-by-worktree
+test_expect_exists     archive/excluded-by-pathspec.d
+test_expect_exists     archive/excluded-by-pathspec.d/file
+
+test_expect_success 'git archive with pathspec' '
+       git archive HEAD ":!excluded-by-pathspec.d" >archive-pathspec.tar &&
+       extract_tar_to_dir archive-pathspec
+'
+
+test_expect_missing    archive-pathspec/ignored
+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
+test_expect_missing    archive-pathspec/excluded-by-pathspec.d/file
+
+test_expect_success 'git archive with wildcard pathspec' '
+       git archive HEAD ":!excluded-by-p*" >archive-pathspec-wildcard.tar &&
+       extract_tar_to_dir archive-pathspec-wildcard
+'
+
+test_expect_missing    archive-pathspec-wildcard/ignored
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree.d
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree.d/file
+test_expect_exists     archive-pathspec-wildcard/ignored-by-worktree
+test_expect_missing    archive-pathspec-wildcard/excluded-by-pathspec.d
+test_expect_missing    archive-pathspec-wildcard/excluded-by-pathspec.d/file
 
 test_expect_success 'git archive with worktree attributes' '
        git archive --worktree-attributes HEAD >worktree.tar &&
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 97a07655a0e0d74747259498bfbb8d03ea6c7480..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
@@ -188,35 +188,29 @@ test_expect_success 'fail to track annotated tags' '
        test_must_fail git checkout heavytrack
 '
 
-test_expect_success 'setup tracking with branch --set-upstream on existing branch' '
+test_expect_success '--set-upstream-to does not change branch' '
        git branch from-master master &&
-       test_must_fail git config branch.from-master.merge > actual &&
-       git branch --set-upstream from-master master &&
-       git config branch.from-master.merge > actual &&
-       grep -q "^refs/heads/master$" actual
-'
-
-test_expect_success '--set-upstream does not change branch' '
+       git branch --set-upstream-to master from-master &&
        git branch from-master2 master &&
        test_must_fail git config branch.from-master2.merge > actual &&
        git rev-list from-master2 &&
        git update-ref refs/heads/from-master2 from-master2^ &&
        git rev-parse from-master2 >expect2 &&
-       git branch --set-upstream from-master2 master &&
+       git branch --set-upstream-to master from-master2 &&
        git config branch.from-master.merge > actual &&
        git rev-parse from-master2 >actual2 &&
        grep -q "^refs/heads/master$" actual &&
        cmp expect2 actual2
 '
 
-test_expect_success '--set-upstream @{-1}' '
-       git checkout from-master &&
+test_expect_success '--set-upstream-to @{-1}' '
+       git checkout follower &&
        git checkout from-master2 &&
        git config branch.from-master2.merge > expect2 &&
-       git branch --set-upstream @{-1} follower &&
+       git branch --set-upstream-to @{-1} from-master &&
        git config branch.from-master.merge > actual &&
        git config branch.from-master2.merge > actual2 &&
-       git branch --set-upstream from-master follower &&
+       git branch --set-upstream-to follower from-master &&
        git config branch.from-master.merge > expect &&
        test_cmp expect2 actual2 &&
        test_cmp expect actual
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 9dd5cde5fc53deb365234e815d7142de9a975005..eb829fce97dc7067cbb502b9f5fd2b14c4e74e10 100755 (executable)
@@ -25,7 +25,7 @@ EOF
        test_cmp expect actual
 '
 
-test_expect_success 'exclude only no longer errors out' '
+test_expect_success 'exclude only pathspec uses default implicit pathspec' '
        git log --oneline --format=%s -- . ":(exclude)sub" >expect &&
        git log --oneline --format=%s -- ":(exclude)sub" >actual &&
        test_cmp expect actual
@@ -183,4 +183,15 @@ EOF
        test_cmp expect actual
 '
 
+test_expect_success 'multiple exclusions' '
+       git ls-files -- ":^*/file2" ":^sub2" >actual &&
+       cat <<-\EOF >expect &&
+       file
+       sub/file
+       sub/sub/file
+       sub/sub/sub/file
+       EOF
+       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 034914a14fd49fea07e8bff4802994fcd38807a9..6f083c4d68b677ff4058961311d6f753d3953a54 100755 (executable)
@@ -406,6 +406,14 @@ test_expect_success 'submodule update - command in .git/config' '
        )
 '
 
+test_expect_success 'submodule update - command in .gitmodules is ignored' '
+       test_when_finished "git -C super reset --hard HEAD^" &&
+       git -C super config -f .gitmodules submodule.submodule.update "!false" &&
+       git -C super commit -a -m "add command to .gitmodules file" &&
+       git -C super/submodule reset --hard $submodulesha1^ &&
+       git -C super submodule update submodule
+'
+
 cat << EOF >expect
 Execution of 'false $submodulesha1' failed in submodule path 'submodule'
 EOF
index 88d4cda2992496d8ff5643caaaccf53e90dedc3a..302a3a2082a9da593ace637a2c32d8f4cf992cb3 100755 (executable)
@@ -101,6 +101,10 @@ cat > "$HOOK" <<EOF
 exit 1
 EOF
 
+commit_msg_is () {
+       test "$(git log --pretty=format:%s%b -1)" = "$1"
+}
+
 test_expect_success 'with failing hook' '
 
        echo "another" >> file &&
@@ -135,6 +139,32 @@ test_expect_success '--no-verify with failing hook (editor)' '
 
 '
 
+test_expect_success 'merge fails with failing hook' '
+
+       test_when_finished "git branch -D newbranch" &&
+       test_when_finished "git checkout -f master" &&
+       git checkout --orphan newbranch &&
+       : >file2 &&
+       git add file2 &&
+       git commit --no-verify file2 -m in-side-branch &&
+       test_must_fail git merge --allow-unrelated-histories master &&
+       commit_msg_is "in-side-branch" # HEAD before merge
+
+'
+
+test_expect_success 'merge bypasses failing hook with --no-verify' '
+
+       test_when_finished "git branch -D newbranch" &&
+       test_when_finished "git checkout -f master" &&
+       git checkout --orphan newbranch &&
+       : >file2 &&
+       git add file2 &&
+       git commit --no-verify file2 -m in-side-branch &&
+       git merge --no-verify --allow-unrelated-histories master &&
+       commit_msg_is "Merge branch '\''master'\'' into newbranch"
+'
+
+
 chmod -x "$HOOK"
 test_expect_success POSIXPERM 'with non-executable hook' '
 
@@ -178,10 +208,6 @@ exit 0
 EOF
 chmod +x "$HOOK"
 
-commit_msg_is () {
-       test "$(git log --pretty=format:%s%b -1)" = "$1"
-}
-
 test_expect_success 'hook edits commit message' '
 
        echo "additional" >> file &&
@@ -217,7 +243,36 @@ test_expect_success "hook doesn't edit commit message (editor)" '
        echo "more plus" > FAKE_MSG &&
        GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify &&
        commit_msg_is "more plus"
+'
 
+test_expect_success 'hook called in git-merge picks up commit message' '
+       test_when_finished "git branch -D newbranch" &&
+       test_when_finished "git checkout -f master" &&
+       git checkout --orphan newbranch &&
+       : >file2 &&
+       git add file2 &&
+       git commit --no-verify file2 -m in-side-branch &&
+       git merge --allow-unrelated-histories master &&
+       commit_msg_is "new message"
+'
+
+test_expect_failure 'merge --continue remembers --no-verify' '
+       test_when_finished "git branch -D newbranch" &&
+       test_when_finished "git checkout -f master" &&
+       git checkout master &&
+       echo a >file2 &&
+       git add file2 &&
+       git commit --no-verify -m "add file2 to master" &&
+       git checkout -b newbranch master^ &&
+       echo b >file2 &&
+       git add file2 &&
+       git commit --no-verify file2 -m in-side-branch &&
+       git merge --no-verify -m not-rewritten-by-hook master &&
+       # resolve conflict:
+       echo c >file2 &&
+       git add file2 &&
+       git merge --continue &&
+       commit_msg_is not-rewritten-by-hook
 '
 
 # set up fake editor to replace `pick` by `reword`
@@ -237,4 +292,5 @@ test_expect_success 'hook is called for reword during `rebase -i`' '
 
 '
 
+
 test_done
index 43d19a9b2292033fd0297afb7df162efa2eb5621..93f162a4f78cf9c54b12309a62f6a89b2ce4ddbb 100755 (executable)
@@ -1670,4 +1670,14 @@ test_expect_success '"Initial commit" should not be noted in commit template' '
        test_i18ngrep ! "Initial commit" output
 '
 
+test_expect_success '--no-optional-locks prevents index update' '
+       test-chmtime =1234567890 .git/index &&
+       git --no-optional-locks status &&
+       test-chmtime -v +0 .git/index >out &&
+       grep ^1234567890 out &&
+       git status &&
+       test-chmtime -v +0 .git/index >out &&
+       ! grep ^1234567890 out
+'
+
 test_done
index d8242e467eaa4f0a46cfa0a4ad431a447e58dd6c..0f86c191745d65c88c70881c6520685289bc2405 100755 (executable)
@@ -51,6 +51,11 @@ test_expect_success '--path=<path> complains without --textconv/--filters' '
        grep "path.*needs.*filters" err
 '
 
+test_expect_success '--textconv/--filters complain without path' '
+       test_must_fail git cat-file --textconv HEAD &&
+       test_must_fail git cat-file --filters HEAD
+'
+
 test_expect_success 'cat-file --textconv --batch works' '
        sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
        test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
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 432c61d246c938192a2b31f2db35f4f9b320a788..c30660d60626c886dfa5993acddaebf2d3364de9 100755 (executable)
@@ -588,4 +588,52 @@ test_expect_success 'cvs annotate' '
     test_cmp ../expect ../actual
 '
 
+#------------
+# running via git-shell
+#------------
+
+cd "$WORKDIR"
+
+test_expect_success 'create remote-cvs helper' '
+       write_script remote-cvs <<-\EOF
+       exec git shell -c "cvs server"
+       EOF
+'
+
+test_expect_success 'cvs server does not run with vanilla git-shell' '
+       (
+               cd cvswork &&
+               CVS_SERVER=$WORKDIR/remote-cvs &&
+               export CVS_SERVER &&
+               test_must_fail cvs log merge
+       )
+'
+
+test_expect_success 'configure git shell to run cvs server' '
+       mkdir "$HOME"/git-shell-commands &&
+
+       write_script "$HOME"/git-shell-commands/cvs <<-\EOF &&
+       if ! test $# = 1 && test "$1" = "server"
+       then
+               echo >&2 "git-cvsserver only handles \"server\""
+               exit 1
+       fi
+       exec git cvsserver server
+       EOF
+
+       # Should not be used, but part of the recommended setup
+       write_script "$HOME"/git-shell-commands/no-interactive-login <<-\EOF
+       echo Interactive login forbidden
+       EOF
+'
+
+test_expect_success 'cvs server can run with recommended config' '
+       (
+               cd cvswork &&
+               CVS_SERVER=$WORKDIR/remote-cvs &&
+               export CVS_SERVER &&
+               cvs log merge
+       )
+'
+
 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 d75ff0514d8d4dd05da1d618ddf2602e5a4f26a9..fb8c01e57a99f35f63690c536e3f623d3695b1de 100644 (file)
@@ -26,7 +26,6 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
                const char *localname;
                const char *tmp;
                const char *remotename;
-               unsigned char sha[20];
                int flag = 0;
                /*
                 * Check suitability for tracking. Must be successful /
@@ -44,7 +43,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
                localname = ref->peer_ref->name;
                remotename = ref->name;
                tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
-                                        sha, &flag);
+                                        NULL, &flag);
                if (tmp && flag & REF_ISSYMREF &&
                        starts_with(tmp, "refs/heads/"))
                        localname = tmp;
index c99309069a90cec08b90ccab62b316a15ddd8672..684f0e33736ca237a933f4def3e1560356bde2c2 100644 (file)
@@ -582,12 +582,11 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s
        int retval = MISSING_OBJECT;
        struct dir_state *parents = NULL;
        size_t parents_alloc = 0;
-       ssize_t parents_nr = 0;
+       size_t i, parents_nr = 0;
        unsigned char current_tree_sha1[20];
        struct strbuf namebuf = STRBUF_INIT;
        struct tree_desc t;
        int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
-       int i;
 
        init_tree_desc(&t, NULL, 0UL);
        strbuf_addstr(&namebuf, name);
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;
index 7efff2fbfd76380fd448d53173f8295d7c9c9d87..e25f725c0feaa57c9a09c976628217c99dfb5652 100644 (file)
@@ -888,7 +888,7 @@ static void receive_needs(void)
                }
 
        shallow_nr += shallows.nr;
-       free(shallows.objects);
+       object_array_clear(&shallows);
 }
 
 /* return non-zero if the ref is hidden, otherwise 0 */
@@ -965,11 +965,10 @@ static int find_symref(const char *refname, const struct object_id *oid,
 {
        const char *symref_target;
        struct string_list_item *item;
-       struct object_id unused;
 
        if ((flag & REF_ISSYMREF) == 0)
                return 0;
-       symref_target = resolve_ref_unsafe(refname, 0, unused.hash, &flag);
+       symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
        if (!symref_target || (flag & REF_ISSYMREF) == 0)
                die("'%s' is a symref but it is not?", refname);
        item = string_list_append(cb_data, refname);
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..dbfb4e13cddceaa44feee51016c9b837f7f4fce1 100644 (file)
@@ -38,7 +38,7 @@ IPATTERN("fortran",
         "|//|\\*\\*|::|[/<>=]="),
 IPATTERN("fountain", "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
         "[^ \t-]+"),
-PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
+PATTERNS("html", "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
         "[^<>= \t]+"),
 PATTERNS("java",
         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
@@ -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..70015629dc69e4bdf7f8ef1973596f3360898da3 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;
@@ -307,7 +307,6 @@ const struct worktree *find_shared_symref(const char *symref,
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
                const char *symref_target;
-               unsigned char sha1[20];
                struct ref_store *refs;
                int flags;
 
@@ -327,7 +326,7 @@ const struct worktree *find_shared_symref(const char *symref,
 
                refs = get_worktree_ref_store(wt);
                symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
-                                                       sha1, &flags);
+                                                       NULL, &flags);
                if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
                        existing = wt;
                        break;
@@ -386,3 +385,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;
 }