Merge branch 'jk/read-in-full'
authorJunio C Hamano <gitster@pobox.com>
Tue, 3 Oct 2017 06:42:49 +0000 (15:42 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 3 Oct 2017 06:42:49 +0000 (15:42 +0900)
Code clean-up to prevent future mistakes by copying and pasting
code that checks the result of read_in_full() function.

* jk/read-in-full:
worktree: check the result of read_in_full()
worktree: use xsize_t to access file size
distinguish error versus short read from read_in_full()
avoid looking at errno for short read_in_full() returns
prefer "!=" when checking read_in_full() result
notes-merge: drop dead zero-write code
files-backend: prefer "0" for write_in_full() error check

143 files changed:
.clang-format [new file with mode: 0644]
.travis.yml
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-branch.txt
Documentation/git-describe.txt
Documentation/git-filter-branch.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-rev-parse.txt
Documentation/git-shell.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/githooks.txt
Documentation/merge-strategies.txt
Documentation/technical/api-string-list.txt [deleted file]
Makefile
abspath.c
apply.c
archive.c
bisect.c
branch.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/commit.c
builtin/describe.c
builtin/fast-export.c
builtin/fsck.c
builtin/gc.c
builtin/help.c
builtin/log.c
builtin/pack-objects.c
builtin/rebase--helper.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote-ext.c
builtin/remote.c
builtin/rev-parse.c
builtin/submodule--helper.c
builtin/symbolic-ref.c
bundle.c
cache.h
ci/install-dependencies.sh [new file with mode: 0755]
ci/lib-travisci.sh [new file with mode: 0755]
ci/print-test-failures.sh [new file with mode: 0755]
ci/run-build.sh [new file with mode: 0755]
ci/run-linux32-docker.sh [new file with mode: 0755]
ci/run-static-analysis.sh [new file with mode: 0755]
ci/run-tests.sh [new file with mode: 0755]
ci/run-windows-build.sh
ci/test-documentation.sh
commit-slab.h
commit.c
compat/win32/lazyload.h [new file with mode: 0644]
config.c
config.h
convert.c
diff-delta.c
diff-lib.c
diff.c
dir.c
environment.c
fsck.c
git-archimport.perl
git-compat-util.h
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-rebase--interactive.sh
git-rebase.sh
git.c
http-backend.c
http-push.c
http.c
imap-send.c
line-log.c
line-log.h
log-tree.c
mailinfo.c
object.c
object.h
pack-bitmap-write.c
pack-bitmap.c
packfile.c
parse-options.c
pathspec.c
pathspec.h
quote.c
read-cache.c
refs.c
refs.h
refs/files-backend.c
refs/packed-backend.c
refs/refs-internal.h
remote.c
revision.c
revision.h
send-pack.c
sequencer.c
sequencer.h
setup.c
sha1_file.c
shallow.c
shell.c
string-list.h
submodule.c
submodule.h
t/README
t/check-non-portable-shell.pl
t/helper/test-line-buffer.c
t/helper/test-parse-options.c
t/t0040-parse-options.sh
t/t1400-update-ref.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/t5001-archive-attr.sh
t/t5002-archive-attr-pattern.sh
t/t5004-archive-corner-cases.sh
t/t5531-deep-submodule-push.sh
t/t6120-describe.sh
t/t7001-mv.sh
t/t7004-tag.sh
t/t7508-status.sh
t/t8010-cat-file-filters.sh
t/t9010-svn-fe.sh
t/t9350-fast-export.sh
t/t9400-git-cvsserver-server.sh
t/test-lib.sh
transport.c
tree-walk.c
upload-pack.c
userdiff.c
worktree.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/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 bcfe78f59dd0cfb24897d986499aa7c8ada12834..bec9186adefe6eeec3d2dd18c871f5fd2047ff6a 100644 (file)
@@ -91,4 +91,15 @@ Fixes since v2.14.1
  * "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 9b5c417b0e958ecceecbdfb94b88d07a4b57a194..8a869e4ef1d6a620bbb76e85540f9e36e6b95fb2 100644 (file)
@@ -80,6 +80,13 @@ UI, Workflows & Features
  * 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.
 
@@ -167,6 +174,34 @@ Performance, Internal Implementation, Development Support etc.
 
  * 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.
 
@@ -296,6 +331,96 @@ Fixes since v2.14
    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 f094b89a4d ma/parse-maybe-bool later to maint).
    (merge 39b00fa4d4 jk/drop-sha1-entry-pos later to maint).
@@ -309,3 +434,10 @@ Fixes since v2.14
    (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 e292737b9c5ab65d53ed3caf2e553a7e7e098fb5..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.
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 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 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 6805a74aec20cacd7b9320136b8648ad5b40ef5b..3cedfb0fd22be1c060ab2e7b9a141a0d1deb03af 100644 (file)
@@ -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 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 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 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 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 1bb4f92d4d317e16929767066a61c123fee0f280..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
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
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 ed5960e6b378a982fb770f428324ba8fec0ba8c6..ed4ca438bd9c6ddab51f78cb4b620f02a7e12eed 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2457,6 +2457,10 @@ $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
 .PHONY: sparse $(SP_OBJ)
 sparse: $(SP_OBJ)
 
+.PHONY: style
+style:
+       git clang-format --style file --diff --extensions c,h
+
 check: common-cmds.h
        @if sparse; \
        then \
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 71cbbd141c73bd72778d71e1e2400413c11f492f..c022af53a24bf56556d136a3456862cf6a2ca7cc 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -2920,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,
@@ -2927,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 1ab8d3a1d7cc9c3e55465fbadf899e8a267de689..1e41f4bbeb1d8730da83aad12bc8987abedcad8a 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -121,11 +121,6 @@ static int check_attr_export_subst(const struct attr_check *check)
        return check && ATTR_TRUE(check->items[1].value);
 }
 
-static int should_queue_directories(const struct archiver_args *args)
-{
-       return args->pathspec.has_wildcard;
-}
-
 static int write_archive_entry(const unsigned char *sha1, const char *base,
                int baselen, const char *filename, unsigned mode, int stage,
                void *context)
@@ -147,7 +142,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                strbuf_addch(&path, '/');
        path_without_prefix = path.buf + args->baselen;
 
-       if (!S_ISDIR(mode) || !should_queue_directories(args)) {
+       if (!S_ISDIR(mode)) {
                const struct attr_check *check;
                check = get_archive_attrs(path_without_prefix);
                if (check_attr_export_ignore(check))
@@ -169,14 +164,6 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
        return write_entry(args, sha1, path.buf, path.len, mode);
 }
 
-static int write_archive_entry_buf(const unsigned char *sha1, struct strbuf *base,
-               const char *filename, unsigned mode, int stage,
-               void *context)
-{
-       return write_archive_entry(sha1, base->buf, base->len,
-                                    filename, mode, stage, context);
-}
-
 static void queue_directory(const unsigned char *sha1,
                struct strbuf *base, const char *filename,
                unsigned mode, int stage, struct archiver_context *c)
@@ -290,9 +277,7 @@ int write_archive_entries(struct archiver_args *args,
        }
 
        err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
-                                 should_queue_directories(args) ?
-                                 queue_or_write_archive_entry :
-                                 write_archive_entry_buf,
+                                 queue_or_write_archive_entry,
                                  &context);
        if (err == READ_TREE_RECURSIVE)
                err = 0;
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 703ded69ca2697f34bc1e225ccecebf33e5ad0b1..4377ce2fb17d7467ee92d4db02cb4859f6397505 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -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."));
        }
index 355f9ef5da164d4d3e88872984fced9e9a744a84..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;
@@ -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) {
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 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 58f9747c2f2d99d2ab4d78f62f5e9c0928a12f1f..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
index e77163e909d07ec168d41d8b96e05afa73490848..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? */
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 c22787ac723c421b17032ecf0b718ff434203c86..3c5eae0edf12e461e84c71c0e2988fcfa58d3d44 100644 (file)
@@ -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 =
index b3f60a8f30b39e9731fcde08600c900b2f772acb..d3c8fc40820faa10f7c9004560159986b2900e9e 100644 (file)
@@ -441,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 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 a57b4f058dff3ceec80355560bf6a856f130b8c0..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;
                }
@@ -2563,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 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 dd06b3fb4f9ee3e4da46d807b183541ec14c5481..29a0f3b75fb02dfa2a02c325816453e36baaa55a 100644 (file)
@@ -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 33ba73933271b51986112107b97b4846e4a913b0..4f5cac96b0f5e0044ddf0fd1e2ea686f694d62ca 100644 (file)
@@ -558,14 +558,13 @@ 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
index 9f24004c0a310d22808c86ab6ee65e6b296fe70c..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;
@@ -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 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 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;
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
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 906298052d485867599a98af1cf49e4728028b50..1e0e633790bb834ad05ed9619e4afa0bac33ce19 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1086,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;
 }
 
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 a77cae56ed26212c6bfbbd831c85c8cf64deb8b9..4831c1273542706437139ccc379afb38e7ca3d3b 100644 (file)
--- a/config.c
+++ b/config.c
@@ -2200,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)
@@ -2292,11 +2292,10 @@ static int write_error(const char *filename)
        return 4;
 }
 
-static ssize_t write_section(int fd, const char *key)
+static struct strbuf store_create_section(const char *key)
 {
        const char *dot;
        int i;
-       ssize_t ret;
        struct strbuf sb = STRBUF_INIT;
 
        dot = memchr(key, '.', store.baselen);
@@ -2312,7 +2311,15 @@ static ssize_t write_section(int fd, const char *key)
                strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
        }
 
-       ret = write_in_full(fd, sb.buf, 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 ret;
@@ -2355,6 +2362,7 @@ static ssize_t write_pair(int fd, const char *key, const char *value)
                case '"':
                case '\\':
                        strbuf_addch(&sb, '\\');
+                       /* fallthrough */
                default:
                        strbuf_addch(&sb, value[i]);
                        break;
@@ -2742,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;
@@ -2752,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);
@@ -2790,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) {
@@ -2803,25 +2830,29 @@ int git_config_rename_section_in_file(const char *config_filename,
                                        continue;
                                }
                                store.baselen = strlen(new_name);
-                               if (write_section(out_fd, new_name) < 0) {
-                                       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;
@@ -2829,11 +2860,30 @@ int git_config_rename_section_in_file(const char *config_filename,
                if (remove)
                        continue;
                length = strlen(output);
+
+               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:
@@ -2849,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 456b3d134d3ed187b80b5941c20690763f3eab35..a49d26441622508fd3c6560ac9784e84541c10dc 100644 (file)
--- a/config.h
+++ b/config.h
@@ -70,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 a09935cb8123f04a8fdb4d48157c2a98e67b3c64..20d7ab67bdf889feb255e01a9b18cf4efa6107a3 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1545,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 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 3c6a3e0faa58f810ce2fe8b27f62581c175edf64..4da0714cb5c4147defe9bb59685cf0f68c63bdc0 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -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;
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);
 
index f1f934b6fddd101191a17b1bc1883da287793319..8289c25b44d74a0f054f9fe3eaf44c5fdcf0b3d2 100644 (file)
@@ -338,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);
+}
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 9bc15b0363d562fc55268419055e9cdbbeee50c3..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)))
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 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
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 e51c7805c805ac6c1234665f5f90fcdd51d39231..f3dc218b2a3d2662efda56fb6c8878a37a4e67a4 100644 (file)
@@ -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;
 };
 
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 f2387a32675591d6da83473ef6235ba823721d1c..a89db22ab0c8ef50ae31d5811de5fc48d09224a2 100644 (file)
@@ -367,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;
@@ -822,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;
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 *);
 
 /*
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 263efb7151a4c4b1ef2dbc96d53b0636ff8ce287..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; }
@@ -868,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;
@@ -1839,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)
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,
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 cdcd11c71efd67130dd8159c9ac4da86e854222a..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;
        }
diff --git a/refs.c b/refs.c
index e8b67ddce80c83f8ec0dbfb3adc6d6c10b2f4b2d..6cfd88ff6b581e3d60c7f81e2e8d74dafee6fc84 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)
@@ -939,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,
@@ -1357,7 +1357,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)
@@ -1396,9 +1396,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;
 
@@ -2033,3 +2036,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 78a26400b61b0046c72a64b985b9aa6443f27021..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:
@@ -344,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.
  */
@@ -433,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 e35c64c001d98582f72945b3af9ab3bc5261273b..4b46cd2e26dbcb782d58bcf4a7bc0c0494b715a4 100644 (file)
@@ -1258,9 +1258,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");
@@ -1292,8 +1292,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)) {
@@ -1301,13 +1305,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;
@@ -1320,7 +1330,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,
@@ -1351,7 +1361,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;
        }
@@ -1402,6 +1415,22 @@ static int files_rename_ref(struct ref_store *ref_store,
        return ret;
 }
 
+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_gently(&lock->lk))
@@ -1676,13 +1705,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;
@@ -3065,6 +3093,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 3bc47ffd5ea4e82f2505a8649b162d18ebf11a21..9c0d685c7f4e8d770f151ddd9e0aeac48b9e252b 100644 (file)
@@ -966,6 +966,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();
@@ -1031,6 +1038,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 d7d344de73e6f85fed1e8460bce246a894eab6da..8821e27ed9c033d36caceefc9cd1242682ae9398 100644 (file)
@@ -559,6 +559,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
@@ -657,6 +660,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 41130900698893f46dd18b817a7e6b6485c735a4..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);
 }
index f9a90d71d2008415e80b30c9e5d3b2743af5438f..d167223e694e54626786740954454d4973e28752 100644 (file)
@@ -21,6 +21,7 @@
 #include "bisect.h"
 #include "packfile.h"
 #include "worktree.h"
+#include "argv-array.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -1105,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;
@@ -1672,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;
@@ -2263,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"));
 
@@ -2286,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;
 
@@ -2305,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;
                }
@@ -2366,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.
@@ -2388,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 3a3d3e2cf824bf0ae404204c1ea81dad813d5f93..54761200adf2d5111b8aba097299bd2fd080a949 100644 (file)
@@ -150,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 b865f662e41880d11305ac6a4fe64450ef26f133..a8cc6b266e67847abafa613184752b3b781eb8dd 100644 (file)
@@ -497,7 +497,7 @@ int send_pack(struct send_pack_args *args,
                                strbuf_release(&cap_buf);
                                return atomic_push_failure(args, remote_refs, ref);
                        }
-                       /* Fallthrough for non atomic case. */
+                       /* else fallthrough */
                default:
                        continue;
                }
index 60636ce54b615e19a22dc2396211d56540b4e920..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"
 
@@ -2435,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 6d8380acd2b66ee7d8206639d4b03933afb1816e..d777ff34b7daf50fb24a8653a7e26e3e95b8c0c7 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -541,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 11346cf6d83e69cfb39ab059de8cae6f6c982c64..09ad64ce555e0b908ce0d9f22219026a2d37ae0c 100644 (file)
@@ -398,7 +398,7 @@ static const char *parse_alt_odb_entry(const char *string,
        return end;
 }
 
-static void link_alt_odb_entries(const char *alt, int len, int sep,
+static void link_alt_odb_entries(const char *alt, int sep,
                                 const char *relative_base, int depth)
 {
        struct strbuf objdirbuf = STRBUF_INIT;
@@ -427,28 +427,19 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
 
 static void read_info_alternates(const char * relative_base, int depth)
 {
-       char *map;
-       size_t mapsz;
-       struct stat st;
        char *path;
-       int fd;
+       struct strbuf buf = STRBUF_INIT;
 
        path = xstrfmt("%s/info/alternates", relative_base);
-       fd = git_open(path);
-       free(path);
-       if (fd < 0)
-               return;
-       if (fstat(fd, &st) || (st.st_size == 0)) {
-               close(fd);
+       if (strbuf_read_file(&buf, path, 1024) < 0) {
+               warn_on_fopen_errors(path);
+               free(path);
                return;
        }
-       mapsz = xsize_t(st.st_size);
-       map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);
-       close(fd);
-
-       link_alt_odb_entries(map, mapsz, '\n', relative_base, depth);
 
-       munmap(map, mapsz);
+       link_alt_odb_entries(buf.buf, '\n', relative_base, depth);
+       strbuf_release(&buf);
+       free(path);
 }
 
 struct alternate_object_database *alloc_alt_odb(const char *dir)
@@ -503,7 +494,7 @@ void add_to_alternates_file(const char *reference)
                if (commit_lock_file(lock))
                        die_errno("unable to move new alternates file into place");
                if (alt_odb_tail)
-                       link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+                       link_alt_odb_entries(reference, '\n', NULL, 0);
        }
        free(alts);
 }
@@ -516,7 +507,7 @@ void add_to_alternates_memory(const char *reference)
         */
        prepare_alt_odb();
 
-       link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+       link_alt_odb_entries(reference, '\n', NULL, 0);
 }
 
 /*
@@ -619,7 +610,7 @@ void prepare_alt_odb(void)
        if (!alt) alt = "";
 
        alt_odb_tail = &alt_odb_list;
-       link_alt_odb_entries(alt, strlen(alt), PATH_SEP, NULL, 0);
+       link_alt_odb_entries(alt, PATH_SEP, NULL, 0);
 
        read_info_alternates(get_object_directory(), 0);
 }
index eabb65d3a7c286832f5a93bb732e8c9c94fa772e..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;
                        }
                }
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 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 075c55f3ca8101e06731ef3b45e51d11ec00c7f6..3e3ea11761fda1c48a3cdd88e146a0f20338c364 100644 (file)
@@ -774,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'.
@@ -802,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
@@ -821,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)
@@ -1668,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;
 }
 
@@ -1773,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;
 }
 
@@ -1980,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 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 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 d971649979fa2661923d9df797125cd4d997cf97..3ac7ebf85f3198cde56f784456e0024205aec853 100755 (executable)
@@ -381,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 &&
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 897f6f06d55a9790a237b75c12eaab95763d1864..e9aa97117ab41beab708f4ded34628b015021e4e 100755 (executable)
@@ -73,7 +73,7 @@ test_expect_missing   archive-pathspec/ignored-by-tree
 test_expect_missing    archive-pathspec/ignored-by-tree.d
 test_expect_missing    archive-pathspec/ignored-by-tree.d/file
 test_expect_exists     archive-pathspec/ignored-by-worktree
-test_expect_missing    archive-pathspec/excluded-by-pathspec.d failure
+test_expect_missing    archive-pathspec/excluded-by-pathspec.d
 test_expect_missing    archive-pathspec/excluded-by-pathspec.d/file
 
 test_expect_success 'git archive with wildcard pathspec' '
index 6667d159ab0950f10a3271f1cb81a63953214f17..bda6d7d7e9e835213f9b1323407222850db47ad6 100755 (executable)
@@ -76,7 +76,7 @@ test_expect_missing   archive/deep/and/slashless/ &&
 test_expect_missing    archive/deep/and/slashless/foo &&
 test_expect_missing    archive/deep/with/wildcard/ &&
 test_expect_missing    archive/deep/with/wildcard/foo &&
-test_expect_exists     archive/one-level-lower/
+test_expect_missing    archive/one-level-lower/
 test_expect_missing    archive/one-level-lower/two-levels-lower/ignored-only-if-dir/
 test_expect_missing    archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir
 
index f6207f42b5a4ed382b819cb81631cd0acab61463..ced44355cab99fc4f5fd9768daf10f5a2c1b21c7 100755 (executable)
@@ -108,14 +108,14 @@ test_expect_success 'archive empty subtree with no pathspec' '
        git archive --format=tar $root_tree >subtree-all.tar &&
        make_dir extract &&
        "$TAR" xf subtree-all.tar -C extract &&
-       check_dir extract sub
+       check_dir extract
 '
 
 test_expect_success 'archive empty subtree by direct pathspec' '
        git archive --format=tar $root_tree -- sub >subtree-path.tar &&
        make_dir extract &&
        "$TAR" xf subtree-path.tar -C extract &&
-       check_dir extract sub
+       check_dir extract
 '
 
 ZIPINFO=zipinfo
index 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 dd6dd9df9b52647381fb4bb6ff2a1cdc1ade7469..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) &&
@@ -279,7 +310,6 @@ test_expect_success 'describe ignoring a borken submodule' '
        grep broken out
 '
 
-# we require ulimit, this excludes Windows
 test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
        i=1 &&
        while test $i -lt 8000
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 5bf5ace56bfde11e9b080db09e45db8df26a539f..b545c33f83be94608f18182444ffe37eef753e88 100755 (executable)
@@ -1863,7 +1863,6 @@ test_expect_success 'version sort with very long prerelease suffix' '
        git tag -l --sort=version:refname
 '
 
-# 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 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 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 83f5d3dd21bbb7cae8b41bf2404f97bb43979d2c..9b61f16f7a8807ad96014d79f1d1b968397584bf 100644 (file)
@@ -1067,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 '
@@ -1170,13 +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 'run_with_limited_stack true'
+test_lazy_prereq ULIMIT_STACK_SIZE '
+       test_have_prereq !MINGW,!CYGWIN &&
+       run_with_limited_stack true
+'
 
 build_option () {
        git version --build-options |
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 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);
index 6321103ce25dea936eea84fdb2a0d6d636e1cddb..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"
index 8aaeea03771cfefa9edd019d0dc5146fef17a9c7..70015629dc69e4bdf7f8ef1973596f3360898da3 100644 (file)
@@ -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;
index ac972acbab9650266c19c16e6a761903ff21250c..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);