Merge branch 'dl/credential-cache-socket-in-xdg-cache'
authorJunio C Hamano <gitster@pobox.com>
Fri, 24 Mar 2017 20:07:34 +0000 (13:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 24 Mar 2017 20:07:34 +0000 (13:07 -0700)
The default location "~/.git-credential-cache/socket" for the
socket used to communicate with the credential-cache daemon has
been moved to "~/.cache/git/credential/socket".

* dl/credential-cache-socket-in-xdg-cache:
credential-cache: add tests for XDG functionality
credential-cache: use XDG_CACHE_HOME for socket
path.c: add xdg_cache_home

116 files changed:
.travis.yml
Documentation/RelNotes/2.12.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.12.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.13.0.txt
Documentation/config.txt
Documentation/git-commit.txt
Documentation/git-filter-branch.txt
Documentation/git-format-patch.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/glossary-content.txt
Makefile
attr.c
attr.h
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/count-objects.c
builtin/describe.c
builtin/diff-tree.c
builtin/difftool.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/index-pack.c
builtin/merge-base.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/prune-packed.c
builtin/prune.c
builtin/receive-pack.c
builtin/reflog.c
builtin/replace.c
builtin/rev-parse.c
builtin/revert.c
builtin/update-index.c
cache.h
ci/run-linux32-build.sh [new file with mode: 0755]
config.c
contrib/completion/git-prompt.sh
dir.c
environment.c
fast-import.c
fetch-pack.c
fetch-pack.h
git-add--interactive.perl
git-compat-util.h
git-filter-branch.sh
hash.h [new file with mode: 0644]
hex.c
http-walker.c
pager.c
pathspec.c
pathspec.h
reachable.c
read-cache.c
ref-filter.c
ref-filter.h
reflog-walk.c
refs.c
refs.h
refs/files-backend.c
remote.h
revision.c
send-pack.c
sequencer.c
setup.c
sha1_file.c
sha1_name.c
split-index.c
split-index.h
strbuf.h
submodule-config.c
submodule.c
submodule.h
t/gitweb-lib.sh
t/helper/test-config.c
t/interop/.gitignore [new file with mode: 0644]
t/interop/Makefile [new file with mode: 0644]
t/interop/README [new file with mode: 0644]
t/interop/i0000-basic.sh [new file with mode: 0755]
t/interop/i5500-git-daemon.sh [new file with mode: 0755]
t/interop/interop-lib.sh [new file with mode: 0644]
t/lib-git-daemon.sh
t/perf/p0001-rev-list.sh
t/perf/p7000-filter-branch.sh
t/perf/perf-lib.sh
t/perf/run
t/t0100-previous.sh
t/t1305-config-include.sh
t/t1309-early-config.sh [new file with mode: 0755]
t/t1500-rev-parse.sh
t/t1700-split-index.sh
t/t3200-branch.sh
t/t3204-branch-name-interpretation.sh [new file with mode: 0755]
t/t3502-cherry-pick-merge.sh
t/t3701-add-interactive.sh
t/t5500-fetch-pack.sh
t/t5516-fetch-push.sh
t/t6135-pathspec-with-attrs.sh [new file with mode: 0755]
t/t7003-filter-branch.sh
t/t7006-pager.sh
t/t7800-difftool.sh
t/t9200-git-cvsexportcommit.sh
t/t9600-cvsimport.sh
transport.c
wt-status.c
index 9c63c8c3f6807841df13161f76d476deca0d94fd..591cc57b80aa415be9c20b3c0dc746d792e68071 100644 (file)
@@ -39,6 +39,27 @@ env:
 
 matrix:
   include:
+    - env: Linux32
+      os: linux
+      services:
+        - docker
+      before_install:
+        - docker pull daald/ubuntu32:xenial
+      before_script:
+      script:
+        - >
+          docker run
+          --interactive
+          --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
     - env: Documentation
       os: linux
       compiler: clang
diff --git a/Documentation/RelNotes/2.12.1.txt b/Documentation/RelNotes/2.12.1.txt
new file mode 100644 (file)
index 0000000..a74f7db
--- /dev/null
@@ -0,0 +1,41 @@
+Git v2.12.1 Release Notes
+=========================
+
+Fixes since v2.12
+-----------------
+
+ * Reduce authentication round-trip over HTTP when the server supports
+   just a single authentication method.  This also improves the
+   behaviour when Git is misconfigured to enable http.emptyAuth
+   against a server that does not authenticate without a username
+   (i.e. not using Kerberos etc., which makes http.emptyAuth
+   pointless).
+
+ * Windows port wants to use OpenSSL's implementation of SHA-1
+   routines, so let them.
+
+ * Add 32-bit Linux variant to the set of platforms to be tested with
+   Travis CI.
+
+ * When a redirected http transport gets an error during the
+   redirected request, we ignored the error we got from the server,
+   and ended up giving a not-so-useful error message.
+
+ * The patch subcommand of "git add -i" was meant to have paths
+   selection prompt just like other subcommand, unlike "git add -p"
+   directly jumps to hunk selection.  Recently, this was broken and
+   "add -i" lost the paths selection dialog, but it now has been
+   fixed.
+
+ * Git v2.12 was shipped with an embarrassing breakage where various
+   operations that verify paths given from the user stopped dying when
+   seeing an issue, and instead later triggering segfault.
+
+ * The code to parse "git log -L..." command line was buggy when there
+   are many ranges specified with -L; overrun of the allocated buffer
+   has been fixed.
+
+ * The command-line parsing of "git log -L" copied internal data
+   structures using incorrect size on ILP32 systems.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.12.2.txt b/Documentation/RelNotes/2.12.2.txt
new file mode 100644 (file)
index 0000000..9efc348
--- /dev/null
@@ -0,0 +1,61 @@
+Git v2.12.2 Release Notes
+=========================
+
+Fixes since v2.12.1
+-------------------
+
+ * "git status --porcelain" is supposed to give a stable output, but a
+   few strings were left as translatable by mistake.
+
+ * "Dumb http" transport used to misparse a nonsense http-alternates
+   response, which has been fixed.
+
+ * "git diff --quiet" relies on the size field in diff_filespec to be
+   correctly populated, but diff_populate_filespec() helper function
+   made an incorrect short-cut when asked only to populate the size
+   field for paths that need to go through convert_to_git() (e.g. CRLF
+   conversion).
+
+ * There is no need for Python only to give a few messages to the
+   standard error stream, but we somehow did.
+
+ * A leak in a codepath to read from a packed object in (rare) cases
+   has been plugged.
+
+ * "git upload-pack", which is a counter-part of "git fetch", did not
+   report a request for a ref that was not advertised as invalid.
+   This is generally not a problem (because "git fetch" will stop
+   before making such a request), but is the right thing to do.
+
+ * A "gc.log" file left by a backgrounded "gc --auto" disables further
+   automatic gc; it has been taught to run at least once a day (by
+   default) by ignoring a stale "gc.log" file that is too old.
+
+ * "git remote rm X", when a branch has remote X configured as the
+   value of its branch.*.remote, tried to remove branch.*.remote and
+   branch.*.merge and failed if either is unset.
+
+ * A caller of tempfile API that uses stdio interface to write to
+   files may ignore errors while writing, which is detected when
+   tempfile is closed (with a call to ferror()).  By that time, the
+   original errno that may have told us what went wrong is likely to
+   be long gone and was overwritten by an irrelevant value.
+   close_tempfile() now resets errno to EIO to make errno at least
+   predictable.
+
+ * "git show-branch" expected there were only very short branch names
+   in the repository and used a fixed-length buffer to hold them
+   without checking for overflow.
+
+ * The code that parses header fields in the commit object has been
+   updated for (micro)performance and code hygiene.
+
+ * A test that creates a confusing branch whose name is HEAD has been
+   corrected not to do so.
+
+ * "Cc:" on the trailer part does not have to conform to RFC strictly,
+   unlike in the e-mail header.  "git send-email" has been updated to
+   ignore anything after '>' when picking addresses, to allow non-address
+   cruft like " # stable 4.4" after the address.
+
+Also contains various documentation updates and code clean-ups.
index 8d5e887c163304ab2d72d3af164839c282ea4276..63e2e8add9e0f40d63be28a208548a658f52f961 100644 (file)
@@ -77,7 +77,6 @@ UI, Workflows & Features
    unlike in the e-mail header.  "git send-email" has been updated to
    ignore anything after '>' when picking addresses, to allow non-address
    cruft like " # stable 4.4" after the address.
-   (merge 9d3343961b jh/send-email-one-cc later to maint).
 
  * When "git submodule init" decides that the submodule in the working
    tree is its upstream, it now gives a warning as it is not a very
@@ -90,6 +89,41 @@ UI, Workflows & Features
 
  * Documentation for "git ls-files" did not refer to core.quotePath.
 
+ * The experimental "split index" feature has gained a few
+   configuration variables to make it easier to use.
+
+ * From a working tree of a repository, a new option of "rev-parse"
+   lets you ask if the repository is used as a submodule of another
+   project, and where the root level of the working tree of that
+   project (i.e. your superproject) is.
+
+ * The pathspec mechanism learned to further limit the paths that
+   match the pattern to those that have specified attributes attached
+   via the gitattributes mechanism.
+
+ * Our source code has used the SHA1_HEADER cpp macro after "#include"
+   in the C code to switch among the SHA-1 implementations. Instead,
+   list the exact header file names and switch among implementations
+   using "#ifdef BLK_SHA1/#include "block-sha1/sha1.h"/.../#endif";
+   this helps some IDE tools.
+
+ * The start-up sequence of "git" needs to figure out some configured
+   settings before it finds and set itself up in the location of the
+   repository and was quite messy due to its "chicken-and-egg" nature.
+   The code has been restructured.
+
+ * The command line prompt (in contrib/) learned a new 'tag' style
+   that can be specified with GIT_PS1_DESCRIBE_STYLE, to describe a
+   detached HEAD with "git describe --tags".
+
+ * The configuration file learned a new "includeIf.<condition>.path"
+   that includes the contents of the given path only when the
+   condition holds.  This allows you to say "include this work-related
+   bit only in the repositories under my ~/work/ directory".
+
+ * Recent update to "rebase -i" started showing a message that is not
+   a warning with "warning:" prefix by mistake.  This has been fixed.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -118,11 +152,9 @@ Performance, Internal Implementation, Development Support etc.
 
  * A test that creates a confusing branch whose name is HEAD has been
    corrected not to do so.
-   (merge f0252ca23c jk/t6300-cleanup later to maint).
 
  * The code that parses header fields in the commit object has been
    updated for (micro)performance and code hygiene.
-   (merge b072504ce1 rs/commit-parsing-optim later to maint).
 
  * An helper function to make it easier to append the result from
    real_path() to a strbuf has been added.
@@ -134,11 +166,34 @@ Performance, Internal Implementation, Development Support etc.
    against a server that does not authenticate without a username
    (i.e. not using Kerberos etc., which makes http.emptyAuth
    pointless).
-   (merge 40a18fc77c jk/http-auth later to maint).
 
  * Windows port wants to use OpenSSL's implementation of SHA-1
    routines, so let them.
-   (merge 2cfc70f0de jh/mingw-openssl-sha1 later to maint).
+
+ * The t/perf performance test suite was not prepared to test not so
+   old versions of Git, but now it covers versions of Git that are not
+   so ancient.
+   (merge 28e1fb5466 jt/perf-updates later to maint).
+
+ * Add 32-bit Linux variant to the set of platforms to be tested with
+   Travis CI.
+
+ * "git branch --list" takes the "--abbrev" and "--no-abbrev" options
+   to control the output of the object name in its "-v"(erbose)
+   output, but a recent update started ignoring them; fix it before
+   the breakage reaches to any released version.
+
+ * Picking two versions of Git and running tests to make sure the
+   older one and the newer one interoperate happily has now become
+   possible.
+   (merge bd4d9d993c jk/interop-test later to maint).
+
+ * "uchar [40]" to "struct object_id" conversion continues.
+
+ * "git tag --contains" used to (ab)use the object bits to keep track
+   of the state of object reachability without clearing them after
+   use; this has been cleaned up and made to use the newer commit-slab
+   facility.
 
 
 Also contains various documentation updates and code clean-ups.
@@ -170,7 +225,6 @@ notes for details).
  * "git show-branch" expected there were only very short branch names
    in the repository and used a fixed-length buffer to hold them
    without checking for overflow.
-   (merge d3cc5f4c44 jk/show-branch-lift-name-len-limit later to maint).
 
  * A caller of tempfile API that uses stdio interface to write to
    files may ignore errors while writing, which is detected when
@@ -179,17 +233,14 @@ notes for details).
    be long gone and was overwritten by an irrelevant value.
    close_tempfile() now resets errno to EIO to make errno at least
    predictable.
-   (merge 7e8c9355b7 jk/tempfile-ferror-fclose-confusion later to maint).
 
  * "git remote rm X", when a branch has remote X configured as the
    value of its branch.*.remote, tried to remove branch.*.remote and
    branch.*.merge and failed if either is unset.
-   (merge 20690b2139 rl/remote-allow-missing-branch-name-merge later to maint).
 
  * A "gc.log" file left by a backgrounded "gc --auto" disables further
    automatic gc; it has been taught to run at least once a day (by
    default) by ignoring a stale "gc.log" file that is too old.
-   (merge a831c06a2b dt/gc-ignore-old-gc-logs later to maint).
 
  * The code to parse "git -c VAR=VAL cmd" and set configuration
    variable for the duration of cmd had two small bugs, which have
@@ -204,52 +255,94 @@ notes for details).
    report a request for a ref that was not advertised as invalid.
    This is generally not a problem (because "git fetch" will stop
    before making such a request), but is the right thing to do.
-   (merge bdb31eada7 jt/upload-pack-error-report later to maint).
 
  * A leak in a codepath to read from a packed object in (rare) cases
    has been plugged.
-   (merge 886ddf4777 rs/sha1-file-plug-fallback-base-leak later to maint).
 
  * When a redirected http transport gets an error during the
    redirected request, we ignored the error we got from the server,
    and ended up giving a not-so-useful error message.
-   (merge 8e27391a5f jt/http-base-url-update-upon-redirect later to maint).
 
  * The patch subcommand of "git add -i" was meant to have paths
    selection prompt just like other subcommand, unlike "git add -p"
    directly jumps to hunk selection.  Recently, this was broken and
    "add -i" lost the paths selection dialog, but it now has been
    fixed.
-   (merge c852bd54bd jk/add-i-patch-do-prompt later to maint).
 
  * Git v2.12 was shipped with an embarrassing breakage where various
    operations that verify paths given from the user stopped dying when
    seeing an issue, and instead later triggering segfault.
-   (merge ce83eadd9a js/realpath-pathdup-fix later to maint).
 
  * There is no need for Python only to give a few messages to the
    standard error stream, but we somehow did.
-   (merge b8686c661d ss/remote-bzr-hg-placeholder-wo-python later to maint).
 
  * The code to parse "git log -L..." command line was buggy when there
    are many ranges specified with -L; overrun of the allocated buffer
    has been fixed.
-   (merge aaae0bf787 ax/line-log-range-merge-fix later to maint).
 
  * The command-line parsing of "git log -L" copied internal data
    structures using incorrect size on ILP32 systems.
-   (merge 07f546cda5 vn/line-log-memcpy-size-fix later to maint).
 
  * "git diff --quiet" relies on the size field in diff_filespec to be
    correctly populated, but diff_populate_filespec() helper function
    made an incorrect short-cut when asked only to populate the size
    field for paths that need to go through convert_to_git() (e.g. CRLF
    conversion).
-   (merge 12426e114b jc/diff-populate-filespec-size-only-fix later to maint).
+
+ * A few tests were run conditionally under (rare) conditions where
+   they cannot be run (like running cvs tests under 'root' account).
+   (merge c6507484a2 ab/cond-skip-tests later to maint).
+
+ * "git branch @" created refs/heads/@ as a branch, and in general the
+   code that handled @{-1} and @{upstream} was a bit too loose in
+   disambiguating.
+   (merge fd4692ff70 jk/interpret-branch-name later to maint).
+
+ * "git fetch" that requests a commit by object name, when the other
+   side does not allow such an request, failed without much
+   explanation.
+   (merge d56583ded6 mm/fetch-show-error-message-on-unadvertised-object later to maint).
+
+ * "git filter-branch --prune-empty" drops a single-parent commit that
+   becomes a no-op, but did not drop a root commit whose tree is empty.
+   (merge 32da7467eb dp/filter-branch-prune-empty later to maint).
+
+ * Recent versions of Git treats http alternates (used in dumb http
+   transport) just like HTTP redirects and requires the client to
+   enable following it, due to security concerns.  But we forgot to
+   give a warning when we decide not to honor the alternates.
+   (merge 5cae73d5d2 ew/http-alternates-as-redirects-warning later to maint).
+
+ * "git push" had a handful of codepaths that could lead to a deadlock
+   when unexpected error happened, which has been fixed.
+   (merge d1a13d3fcb jk/push-deadlock-regression-fix later to maint).
+
+ * "Dumb http" transport used to misparse a nonsense http-alternates
+   response, which has been fixed.
+
+ * "git add -p <pathspec>" unnecessarily expanded the pathspec to a
+   list of individual files that matches the pathspec by running "git
+   ls-files <pathspec>", before feeding it to "git diff-index" to see
+   which paths have changes, because historically the pathspec
+   language supported by "diff-index" was weaker.  These days they are
+   equivalent and there is no reason to internally expand it.  This
+   helps both performance and avoids command line argument limit on
+   some platforms.
+   (merge 7288e12cce jk/add-i-use-pathspecs later to maint).
+
+ * "git status --porcelain" is supposed to give a stable output, but a
+   few strings were left as translatable by mistake.
+
+ * Code to read submodule.<name>.ignore config did not state the
+   variable name correctly when giving an error message diagnosing
+   misconfiguration.
+   (merge 5ea304896e sb/submodule-config-parse-ignore-fix later to maint).
 
  * Other minor doc, test and build updates and code cleanups.
-   (merge 2cfa83574c mm/two-more-xstrfmt later to maint).
-   (merge b803ae4427 ps/docs-diffcore later to maint).
-   (merge bcd886d897 ew/markdown-url-in-readme later to maint).
-   (merge b2d593a779 rj/remove-unused-mktemp later to maint).
-   (merge 3255e512a8 jk/ewah-use-right-type-in-sizeof later to maint).
+   (merge dfa3ad3238 rs/blame-code-cleanup later to maint).
+   (merge ffddfc6328 jk/rev-parse-cleanup later to maint).
+   (merge f20754802a jk/pack-name-cleanups later to maint).
+   (merge d4aae459cd sb/wt-status-cleanup later to maint).
+   (merge 2c7ee986c7 ab/doc-no-option-notation-fix later to maint).
+   (merge e4e016f65d ab/push-default-doc-fix later to maint).
+   (merge baced9e4e5 nd/commit-hook-doc-fix later to maint).
index 5e5c2ae5fb378dc3226e58a4a0df7c3c63472f60..0d8df5a9f95b9550d34d000027f2974ff93f4de6 100644 (file)
@@ -79,18 +79,69 @@ escape sequences) are invalid.
 Includes
 ~~~~~~~~
 
-You can include one config file from another by setting the special
+You can include a config file from another by setting the special
 `include.path` variable to the name of the file to be included. The
 variable takes a pathname as its value, and is subject to tilde
-expansion.
+expansion. `include.path` can be given multiple times.
 
-The
-included file is expanded immediately, as if its contents had been
+The included file is expanded immediately, as if its contents had been
 found at the location of the include directive. If the value of the
-`include.path` variable is a relative path, the path is considered to be
-relative to the configuration file in which the include directive was
-found.  See below for examples.
+`include.path` variable is a relative path, the path is considered to
+be relative to the configuration file in which the include directive
+was found.  See below for examples.
 
+Conditional includes
+~~~~~~~~~~~~~~~~~~~~
+
+You can include a config file from another conditionally by setting a
+`includeIf.<condition>.path` variable to the name of the file to be
+included. The variable's value is treated the same way as
+`include.path`. `includeIf.<condition>.path` can be given multiple times.
+
+The condition starts with a keyword followed by a colon and some data
+whose format and meaning depends on the keyword. Supported keywords
+are:
+
+`gitdir`::
+
+       The data that follows the keyword `gitdir:` is used as a glob
+       pattern. If the location of the .git directory matches the
+       pattern, the include condition is met.
++
+The .git location may be auto-discovered, or come from `$GIT_DIR`
+environment variable. If the repository is auto discovered via a .git
+file (e.g. from submodules, or a linked worktree), the .git location
+would be the final location where the .git directory is, not where the
+.git file is.
++
+The pattern can contain standard globbing wildcards and two additional
+ones, `**/` and `/**`, that can match multiple path components. Please
+refer to linkgit:gitignore[5] for details. For convenience:
+
+ * If the pattern starts with `~/`, `~` will be substituted with the
+   content of the environment variable `HOME`.
+
+ * If the pattern starts with `./`, it is replaced with the directory
+   containing the current config file.
+
+ * If the pattern does not start with either `~/`, `./` or `/`, `**/`
+   will be automatically prepended. For example, the pattern `foo/bar`
+   becomes `**/foo/bar` and would match `/any/path/to/foo/bar`.
+
+ * If the pattern ends with `/`, `**` will be automatically added. For
+   example, the pattern `foo/` becomes `foo/**`. In other words, it
+   matches "foo" and everything inside, recursively.
+
+`gitdir/i`::
+       This is the same as `gitdir` except that matching is done
+       case-insensitively (e.g. on case-insensitive file sytems)
+
+A few more notes on matching via `gitdir` and `gitdir/i`:
+
+ * Symlinks in `$GIT_DIR` are not resolved before matching.
+
+ * Note that "../" is not special and will match literally, which is
+   unlikely what you want.
 
 Example
 ~~~~~~~
@@ -119,6 +170,17 @@ Example
                path = foo ; expand "foo" relative to the current file
                path = ~/foo ; expand "foo" in your `$HOME` directory
 
+       ; include if $GIT_DIR is /path/to/foo/.git
+       [includeIf "gitdir:/path/to/foo/.git"]
+               path = /path/to/foo.inc
+
+       ; include for all repositories inside /path/to/group
+       [includeIf "gitdir:/path/to/group/"]
+               path = /path/to/foo.inc
+
+       ; include for all repositories inside $HOME/to/group
+       [includeIf "gitdir:~/to/group/"]
+               path = /path/to/foo.inc
 
 Values
 ~~~~~~
@@ -334,6 +396,10 @@ core.trustctime::
        crawlers and some backup systems).
        See linkgit:git-update-index[1]. True by default.
 
+core.splitIndex::
+       If true, the split-index feature of the index will be used.
+       See linkgit:git-update-index[1]. False by default.
+
 core.untrackedCache::
        Determines what to do about the untracked cache feature of the
        index. It will be kept, if this variable is unset or set to
@@ -2455,6 +2521,8 @@ push.default::
   pushing to the same repository you would normally pull from
   (i.e. central workflow).
 
+* `tracking` - This is a deprecated synonym for `upstream`.
+
 * `simple` - in centralized workflow, work like `upstream` with an
   added safety to refuse to push if the upstream branch's name is
   different from the local one.
@@ -2850,6 +2918,31 @@ showbranch.default::
        The default set of branches for linkgit:git-show-branch[1].
        See linkgit:git-show-branch[1].
 
+splitIndex.maxPercentChange::
+       When the split index feature is used, this specifies the
+       percent of entries the split index can contain compared to the
+       total number of entries in both the split index and the shared
+       index before a new shared index is written.
+       The value should be between 0 and 100. If the value is 0 then
+       a new shared index is always written, if it is 100 a new
+       shared index is never written.
+       By default the value is 20, so a new shared index is written
+       if the number of entries in the split index would be greater
+       than 20 percent of the total number of entries.
+       See linkgit:git-update-index[1].
+
+splitIndex.sharedIndexExpire::
+       When the split index feature is used, shared index files that
+       were not modified since the time this variable specifies will
+       be removed when a new shared index file is created. The value
+       "now" expires all entries immediately, and "never" suppresses
+       expiration altogether.
+       The default value is "2.weeks.ago".
+       Note that a shared index file is considered modified (for the
+       purpose of expiration) each time a new split-index file is
+       either created based on it or read from it.
+       See linkgit:git-update-index[1].
+
 status.relativePaths::
        By default, linkgit:git-status[1] shows paths relative to the
        current directory. Setting this variable to `false` shows paths
index 25dcdcc2890f0a6814c0000c3c8970e02c0717f8..ed0f5b94b3f200676aedb312391a2dd8fde7325d 100644 (file)
@@ -463,7 +463,7 @@ order).  See linkgit:git-var[1] for details.
 HOOKS
 -----
 This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`,
-and `post-commit` hooks.  See linkgit:githooks[5] for more
+`post-commit` and `post-rewrite` hooks.  See linkgit:githooks[5] for more
 information.
 
 FILES
index 0a09698c033fb2e1cdf68aa33eed4f109a034d5f..6e4bb022043faeddd363c899872e7de96c926c1c 100644 (file)
@@ -167,14 +167,12 @@ to other tags will be rewritten to point to the underlying commit.
        project root. Implies <<Remap_to_ancestor>>.
 
 --prune-empty::
-       Some kind of filters will generate empty commits, that left the tree
-       untouched.  This switch allow git-filter-branch to ignore such
-       commits.  Though, this switch only applies for commits that have one
-       and only one parent, it will hence keep merges points. Also, this
-       option is not compatible with the use of `--commit-filter`. Though you
-       just need to use the function 'git_commit_non_empty_tree "$@"' instead
-       of the `git commit-tree "$@"` idiom in your commit filter to make that
-       happen.
+       Some filters will generate empty commits that leave the tree untouched.
+       This option instructs git-filter-branch to remove such commits if they
+       have exactly one or zero non-pruned parents; merge commits will
+       therefore remain intact.  This option cannot be used together with
+       `--commit-filter`, though the same effect can be achieved by using the
+       provided `git_commit_non_empty_tree` function in a commit filter.
 
 --original <namespace>::
        Use this option to set the namespace where the original commits
index 9b200b379bbc980f37033c68342a287637750583..f7a069bb9234850568d0870fa92ef25d0e1a19b5 100644 (file)
@@ -239,7 +239,7 @@ keeping them as Git notes allows them to be maintained between versions
 of the patch series (but see the discussion of the `notes.rewrite`
 configuration options in linkgit:git-notes[1] to use this workflow).
 
---[no]-signature=<signature>::
+--[no-]signature=<signature>::
        Add a signature to each message produced. Per RFC 3676 the signature
        is separated from the body by a line with '-- ' on it. If the
        signature option is omitted the signature defaults to the Git version
index 91c02b8c856abf2c24af9ea23f0ee01710b8cd12..c40c4704486dd9d1a5650562540db7379818ab03 100644 (file)
@@ -261,6 +261,12 @@ print a message to stderr and exit with nonzero status.
 --show-toplevel::
        Show the absolute path of the top-level directory.
 
+--show-superproject-working-tree
+       Show the absolute path of the root of the superproject's
+       working tree (if exists) that uses the current repository as
+       its submodule.  Outputs nothing if the current repository is
+       not used as a submodule by any project.
+
 --shared-index-path::
        Show the path to the shared index file in split index mode, or
        empty if not in split-index mode.
index 642d0ef199c72160ca74238122fafdbdfee9a0c7..9d66166f69d93e544c949ad80fe0f432fa3f1ffd 100644 (file)
@@ -89,7 +89,7 @@ See the CONFIGURATION section for `sendemail.multiEdit`.
        reply to the given Message-Id, which avoids breaking threads to
        provide a new patch series.
        The second and subsequent emails will be sent as replies according to
-       the `--[no]-chain-reply-to` setting.
+       the `--[no-]chain-reply-to` setting.
 +
 So for example when `--thread` and `--no-chain-reply-to` are specified, the
 second and subsequent patches will be replies to the first one like in the
index 7386c931627f1c2bd1303407075db9831ec4045c..1579abf3c3b45f4e63f8f17746dde31a06a3a564 100644 (file)
@@ -163,14 +163,16 @@ may not support it yet.
 
 --split-index::
 --no-split-index::
-       Enable or disable split index mode. If enabled, the index is
-       split into two files, $GIT_DIR/index and $GIT_DIR/sharedindex.<SHA-1>.
-       Changes are accumulated in $GIT_DIR/index while the shared
-       index file contains all index entries stays unchanged. If
-       split-index mode is already enabled and `--split-index` is
-       given again, all changes in $GIT_DIR/index are pushed back to
-       the shared index file. This mode is designed for very large
-       indexes that take a significant amount of time to read or write.
+       Enable or disable split index mode. If split-index mode is
+       already enabled and `--split-index` is given again, all
+       changes in $GIT_DIR/index are pushed back to the shared index
+       file.
++
+These options take effect whatever the value of the `core.splitIndex`
+configuration variable (see linkgit:git-config[1]). But a warning is
+emitted when the change goes against the configured value, as the
+configured value will take effect next time the index is read and this
+will remove the intended effect of the option.
 
 --untracked-cache::
 --no-untracked-cache::
@@ -388,6 +390,31 @@ Although this bit looks similar to assume-unchanged bit, its goal is
 different from assume-unchanged bit's. Skip-worktree also takes
 precedence over assume-unchanged bit when both are set.
 
+Split index
+-----------
+
+This mode is designed for repositories with very large indexes, and
+aims at reducing the time it takes to repeatedly write these indexes.
+
+In this mode, the index is split into two files, $GIT_DIR/index and
+$GIT_DIR/sharedindex.<SHA-1>. Changes are accumulated in
+$GIT_DIR/index, the split index, while the shared index file contains
+all index entries and stays unchanged.
+
+All changes in the split index are pushed back to the shared index
+file when the number of entries in the split index reaches a level
+specified by the splitIndex.maxPercentChange config variable (see
+linkgit:git-config[1]).
+
+Each time a new shared index file is created, the old shared index
+files are deleted if their modification time is older than what is
+specified by the splitIndex.sharedIndexExpire config variable (see
+linkgit:git-config[1]).
+
+To avoid deleting a shared index file that is still used, its
+modification time is updated to the current time everytime a new split
+index based on the shared index file is either created or read from.
+
 Untracked cache
 ---------------
 
index df0941d456b0fa1ab3be4f459f73edd3145ffb32..3f75af872c040ade3bebdf8ae196e158f0688141 100644 (file)
@@ -44,9 +44,10 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.12.0/git.html[documentation for release 2.12.0]
+* link:v2.12.1/git.html[documentation for release 2.12.1]
 
 * release notes for
+  link:RelNotes/2.12.1.txt[2.12.1].
   link:RelNotes/2.12.0.txt[2.12].
 
 * link:v2.11.1/git.html[documentation for release 2.11.1]
index fc9320e59ecac99fc4d3f6234c74f89aab4dd45b..6e991c246915143deba887632def104fc709c9bf 100644 (file)
@@ -384,6 +384,27 @@ full pathname may have special meaning:
 +
 Glob magic is incompatible with literal magic.
 
+attr;;
+After `attr:` comes a space separated list of "attribute
+requirements", all of which must be met in order for the
+path to be considered a match; this is in addition to the
+usual non-magic pathspec pattern matching.
+See linkgit:gitattributes[5].
++
+Each of the attribute requirements for the path takes one of
+these forms:
+
+- "`ATTR`" requires that the attribute `ATTR` be set.
+
+- "`-ATTR`" requires that the attribute `ATTR` be unset.
+
+- "`ATTR=VALUE`" requires that the attribute `ATTR` be
+  set to the string `VALUE`.
+
+- "`!ATTR`" requires that the attribute `ATTR` be
+  unspecified.
++
+
 exclude;;
        After a path matches any non-exclude pathspec, it will be run
        through all exclude pathspec (magic signature: `!` or its
index ed68700acb19bfe1f24e3143200baf112dd5a7dd..a5a11e721aeb566edb64276e6c10e92b0cfbe574 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1384,19 +1384,19 @@ ifdef APPLE_COMMON_CRYPTO
 endif
 
 ifdef BLK_SHA1
-       SHA1_HEADER = "block-sha1/sha1.h"
        LIB_OBJS += block-sha1/sha1.o
+       BASIC_CFLAGS += -DSHA1_BLK
 else
 ifdef PPC_SHA1
-       SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
+       BASIC_CFLAGS += -DSHA1_PPC
 else
 ifdef APPLE_COMMON_CRYPTO
        COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
-       SHA1_HEADER = <CommonCrypto/CommonDigest.h>
+       BASIC_CFLAGS += -DSHA1_APPLE
 else
-       SHA1_HEADER = <openssl/sha.h>
        EXTLIBS += $(LIB_4_CRYPTO)
+       BASIC_CFLAGS += -DSHA1_OPENSSL
 endif
 endif
 endif
@@ -1588,7 +1588,6 @@ endif
 
 # Shell quote (do not use $(call) to accommodate ancient setups);
 
-SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
 ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
 
@@ -1621,8 +1620,7 @@ PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA))
 # from the dependency list, that would make each entry appear twice.
 LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS)
 
-BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
-       $(COMPAT_CFLAGS)
+BASIC_CFLAGS += $(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
 
 # Quote for C
@@ -2251,6 +2249,9 @@ endif
 ifdef GIT_PERF_MAKE_OPTS
        @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
 endif
+ifdef GIT_INTEROP_MAKE_OPTS
+       @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
+endif
 ifdef TEST_GIT_INDEX_VERSION
        @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
 endif
diff --git a/attr.c b/attr.c
index 5493bff224a98361811c1e9fa88bb16efbf85d22..7e2134471cb4afc1b891208ebe4bebd6ad55571d 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -603,6 +603,23 @@ struct attr_check *attr_check_initl(const char *one, ...)
        return check;
 }
 
+struct attr_check *attr_check_dup(const struct attr_check *check)
+{
+       struct attr_check *ret;
+
+       if (!check)
+               return NULL;
+
+       ret = attr_check_alloc();
+
+       ret->nr = check->nr;
+       ret->alloc = check->alloc;
+       ALLOC_ARRAY(ret->items, ret->nr);
+       COPY_ARRAY(ret->items, check->items, ret->nr);
+
+       return ret;
+}
+
 struct attr_check_item *attr_check_append(struct attr_check *check,
                                          const struct git_attr *attr)
 {
diff --git a/attr.h b/attr.h
index 48ab3e1c2f1c3eaf11e32f345fc833fc160c5b02..442d464db6271da48a4ef133127c7d3a18c186cf 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -44,6 +44,7 @@ struct attr_check {
 
 extern struct attr_check *attr_check_alloc(void);
 extern struct attr_check *attr_check_initl(const char *, ...);
+extern struct attr_check *attr_check_dup(const struct attr_check *check);
 
 extern struct attr_check_item *attr_check_append(struct attr_check *check,
                                                 const struct git_attr *attr);
index cffc62654084b025ffbdb947448fa86c81dc6442..f7aa95f4babaab904443784eccdaa3b219bd2b08 100644 (file)
@@ -658,8 +658,11 @@ static struct origin *find_rename(struct scoreboard *sb,
 /*
  * Append a new blame entry to a given output queue.
  */
-static void add_blame_entry(struct blame_entry ***queue, struct blame_entry *e)
+static void add_blame_entry(struct blame_entry ***queue,
+                           const struct blame_entry *src)
 {
+       struct blame_entry *e = xmalloc(sizeof(*e));
+       memcpy(e, src, sizeof(*e));
        origin_incref(e->suspect);
 
        e->next = **queue;
@@ -760,21 +763,15 @@ static void split_blame(struct blame_entry ***blamed,
                        struct blame_entry *split,
                        struct blame_entry *e)
 {
-       struct blame_entry *new_entry;
-
        if (split[0].suspect && split[2].suspect) {
                /* The first part (reuse storage for the existing entry e) */
                dup_entry(unblamed, e, &split[0]);
 
                /* The last part -- me */
-               new_entry = xmalloc(sizeof(*new_entry));
-               memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
-               add_blame_entry(unblamed, new_entry);
+               add_blame_entry(unblamed, &split[2]);
 
                /* ... and the middle part -- parent */
-               new_entry = xmalloc(sizeof(*new_entry));
-               memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
-               add_blame_entry(blamed, new_entry);
+               add_blame_entry(blamed, &split[1]);
        }
        else if (!split[0].suspect && !split[2].suspect)
                /*
@@ -785,18 +782,12 @@ static void split_blame(struct blame_entry ***blamed,
        else if (split[0].suspect) {
                /* me and then parent */
                dup_entry(unblamed, e, &split[0]);
-
-               new_entry = xmalloc(sizeof(*new_entry));
-               memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
-               add_blame_entry(blamed, new_entry);
+               add_blame_entry(blamed, &split[1]);
        }
        else {
                /* parent and then me */
                dup_entry(blamed, e, &split[1]);
-
-               new_entry = xmalloc(sizeof(*new_entry));
-               memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
-               add_blame_entry(unblamed, new_entry);
+               add_blame_entry(unblamed, &split[2]);
        }
 }
 
index 94f7de7fa5da4a27dab99c582c5dfbbf25169048..52688f2e1be91024ba6cb170b4b38b393e8ab8ba 100644 (file)
@@ -33,7 +33,7 @@ static const char * const builtin_branch_usage[] = {
 };
 
 static const char *head;
-static unsigned char head_sha1[20];
+static struct object_id head_oid;
 
 static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
@@ -118,13 +118,13 @@ static int branch_merged(int kind, const char *name,
        if (kind == FILTER_REFS_BRANCHES) {
                struct branch *branch = branch_get(name);
                const char *upstream = branch_get_upstream(branch, NULL);
-               unsigned char sha1[20];
+               struct object_id oid;
 
                if (upstream &&
                    (reference_name = reference_name_to_free =
                     resolve_refdup(upstream, RESOLVE_REF_READING,
-                                   sha1, NULL)) != NULL)
-                       reference_rev = lookup_commit_reference(sha1);
+                                   oid.hash, NULL)) != NULL)
+                       reference_rev = lookup_commit_reference(oid.hash);
        }
        if (!reference_rev)
                reference_rev = head_rev;
@@ -154,10 +154,10 @@ static int branch_merged(int kind, const char *name,
 }
 
 static int check_branch_commit(const char *branchname, const char *refname,
-                              const unsigned char *sha1, struct commit *head_rev,
+                              const struct object_id *oid, struct commit *head_rev,
                               int kinds, int force)
 {
-       struct commit *rev = lookup_commit_reference(sha1);
+       struct commit *rev = lookup_commit_reference(oid->hash);
        if (!rev) {
                error(_("Couldn't look up commit object for '%s'"), refname);
                return -1;
@@ -184,31 +184,34 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                           int quiet)
 {
        struct commit *head_rev = NULL;
-       unsigned char sha1[20];
+       struct object_id oid;
        char *name = NULL;
        const char *fmt;
        int i;
        int ret = 0;
        int remote_branch = 0;
        struct strbuf bname = STRBUF_INIT;
+       unsigned allowed_interpret;
 
        switch (kinds) {
        case FILTER_REFS_REMOTES:
                fmt = "refs/remotes/%s";
                /* For subsequent UI messages */
                remote_branch = 1;
+               allowed_interpret = INTERPRET_BRANCH_REMOTE;
 
                force = 1;
                break;
        case FILTER_REFS_BRANCHES:
                fmt = "refs/heads/%s";
+               allowed_interpret = INTERPRET_BRANCH_LOCAL;
                break;
        default:
                die(_("cannot use -a with -d"));
        }
 
        if (!force) {
-               head_rev = lookup_commit_reference(head_sha1);
+               head_rev = lookup_commit_reference(head_oid.hash);
                if (!head_rev)
                        die(_("Couldn't look up commit object for HEAD"));
        }
@@ -216,7 +219,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                char *target = NULL;
                int flags = 0;
 
-               strbuf_branchname(&bname, argv[i]);
+               strbuf_branchname(&bname, argv[i], allowed_interpret);
                free(name);
                name = mkpathdup(fmt, bname.buf);
 
@@ -236,7 +239,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                                        RESOLVE_REF_READING
                                        | RESOLVE_REF_NO_RECURSE
                                        | RESOLVE_REF_ALLOW_BAD_NAME,
-                                       sha1, &flags);
+                                       oid.hash, &flags);
                if (!target) {
                        error(remote_branch
                              ? _("remote-tracking branch '%s' not found.")
@@ -246,13 +249,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                }
 
                if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
-                   check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
+                   check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
                                        force)) {
                        ret = 1;
                        goto next;
                }
 
-               if (delete_ref(NULL, name, is_null_sha1(sha1) ? NULL : sha1,
+               if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : oid.hash,
                               REF_NODEREF)) {
                        error(remote_branch
                              ? _("Error deleting remote-tracking branch '%s'")
@@ -268,7 +271,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                               bname.buf,
                               (flags & REF_ISBROKEN) ? "broken"
                               : (flags & REF_ISSYMREF) ? target
-                              : find_unique_abbrev(sha1, DEFAULT_ABBREV));
+                              : find_unique_abbrev(oid.hash, DEFAULT_ABBREV));
                }
                delete_branch_config(bname.buf);
 
@@ -335,9 +338,18 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
                    branch_get_color(BRANCH_COLOR_CURRENT));
 
        if (filter->verbose) {
+               struct strbuf obname = STRBUF_INIT;
+
+               if (filter->abbrev < 0)
+                       strbuf_addf(&obname, "%%(objectname:short)");
+               else if (!filter->abbrev)
+                       strbuf_addf(&obname, "%%(objectname)");
+               else
+                       strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev);
+
                strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
                strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
-               strbuf_addf(&local, " %%(objectname:short=7) ");
+               strbuf_addf(&local, " %s ", obname.buf);
 
                if (filter->verbose > 1)
                        strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
@@ -346,10 +358,12 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
                else
                        strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
 
-               strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)"
-                           "%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)",
+               strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s"
+                           "%%(if)%%(symref)%%(then) -> %%(symref:short)"
+                           "%%(else) %s %%(contents:subject)%%(end)",
                            branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),
-                           branch_get_color(BRANCH_COLOR_RESET));
+                           branch_get_color(BRANCH_COLOR_RESET), obname.buf);
+               strbuf_release(&obname);
        } else {
                strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
                            branch_get_color(BRANCH_COLOR_RESET));
@@ -590,7 +604,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        track = git_branch_track;
 
-       head = resolve_refdup("HEAD", 0, head_sha1, NULL);
+       head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
        if (!head)
                die(_("Failed to resolve HEAD as a valid ref."));
        if (!strcmp(head, "HEAD"))
index 30383e9eb4befb31e0d165d5863615484bcc9da3..8b85cb8cf083e70580f77392cd5e6391318d19c6 100644 (file)
@@ -409,20 +409,20 @@ static int batch_object_cb(const unsigned char sha1[20], void *vdata)
        return 0;
 }
 
-static int batch_loose_object(const unsigned char *sha1,
+static int batch_loose_object(const struct object_id *oid,
                              const char *path,
                              void *data)
 {
-       sha1_array_append(data, sha1);
+       sha1_array_append(data, oid->hash);
        return 0;
 }
 
-static int batch_packed_object(const unsigned char *sha1,
+static int batch_packed_object(const struct object_id *oid,
                               struct packed_git *pack,
                               uint32_t pos,
                               void *data)
 {
-       sha1_array_append(data, sha1);
+       sha1_array_append(data, oid->hash);
        return 0;
 }
 
index f174f503033ea7f1274fc8f09c49c0ae378c9eee..81f07c3ef271db08e36ee7a89b1d128d099ecb96 100644 (file)
@@ -452,7 +452,7 @@ static void setup_branch_path(struct branch_info *branch)
 {
        struct strbuf buf = STRBUF_INIT;
 
-       strbuf_branchname(&buf, branch->name);
+       strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
        if (strcmp(buf.buf, branch->name))
                branch->name = xstrdup(buf.buf);
        strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
index 3f63edbbf94fdf83307621ed822722e5c9f89321..b4c929bb8afbf94967949279a1cc3b9b21dae100 100644 (file)
@@ -681,7 +681,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
 
 static int checkout(int submodule_progress)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        char *head;
        struct lock_file *lock_file;
        struct unpack_trees_options opts;
@@ -692,7 +692,7 @@ static int checkout(int submodule_progress)
        if (option_no_checkout)
                return 0;
 
-       head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL);
+       head = resolve_refdup("HEAD", RESOLVE_REF_READING, oid.hash, NULL);
        if (!head) {
                warning(_("remote HEAD refers to nonexistent ref, "
                          "unable to checkout.\n"));
@@ -700,7 +700,7 @@ static int checkout(int submodule_progress)
        }
        if (!strcmp(head, "HEAD")) {
                if (advice_detached_head)
-                       detach_advice(sha1_to_hex(sha1));
+                       detach_advice(oid_to_hex(&oid));
        } else {
                if (!starts_with(head, "refs/heads/"))
                        die(_("HEAD not found below refs/heads!"));
@@ -721,7 +721,7 @@ static int checkout(int submodule_progress)
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
 
-       tree = parse_tree_indirect(sha1);
+       tree = parse_tree_indirect(oid.hash);
        parse_tree(tree);
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts) < 0)
@@ -731,7 +731,7 @@ static int checkout(int submodule_progress)
                die(_("unable to write new index file"));
 
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
-                          sha1_to_hex(sha1), "1", NULL);
+                          oid_to_hex(&oid), "1", NULL);
 
        if (!err && option_recursive) {
                struct argv_array args = ARGV_ARRAY_INIT;
index 2de5f6cc6401b48852d8174edcbf4e149cbe51cf..4e288bc5132dbc3846382c29b89a15d7bdf7c211 100644 (file)
@@ -496,7 +496,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
                      struct wt_status *s)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
 
        if (s->relative_paths)
                s->prefix = prefix;
@@ -509,9 +509,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
        s->index_file = index_file;
        s->fp = fp;
        s->nowarn = nowarn;
-       s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+       s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0;
        if (!s->is_initial)
-               hashcpy(s->sha1_commit, sha1);
+               hashcpy(s->sha1_commit, oid.hash);
        s->status_format = status_format;
        s->ignore_submodule_arg = ignore_submodule_arg;
 
@@ -885,7 +885,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                commitable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
        } else {
-               unsigned char sha1[20];
+               struct object_id oid;
                const char *parent = "HEAD";
 
                if (!active_nr && read_cache() < 0)
@@ -894,7 +894,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                if (amend)
                        parent = "HEAD^1";
 
-               if (get_sha1(parent, sha1)) {
+               if (get_sha1(parent, oid.hash)) {
                        int i, ita_nr = 0;
 
                        for (i = 0; i < active_nr; i++)
@@ -1332,7 +1332,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 {
        static struct wt_status s;
        int fd;
-       unsigned char sha1[20];
+       struct object_id oid;
        static struct option builtin_status_options[] = {
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_SET_INT('s', "short", &status_format,
@@ -1382,9 +1382,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        fd = hold_locked_index(&index_lock, 0);
 
-       s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
+       s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0;
        if (!s.is_initial)
-               hashcpy(s.sha1_commit, sha1);
+               hashcpy(s.sha1_commit, oid.hash);
 
        s.ignore_submodule_arg = ignore_submodule_arg;
        s.status_format = status_format;
@@ -1418,19 +1418,19 @@ static const char *implicit_ident_advice(void)
 
 }
 
-static void print_summary(const char *prefix, const unsigned char *sha1,
+static void print_summary(const char *prefix, const struct object_id *oid,
                          int initial_commit)
 {
        struct rev_info rev;
        struct commit *commit;
        struct strbuf format = STRBUF_INIT;
-       unsigned char junk_sha1[20];
+       struct object_id junk_oid;
        const char *head;
        struct pretty_print_context pctx = {0};
        struct strbuf author_ident = STRBUF_INIT;
        struct strbuf committer_ident = STRBUF_INIT;
 
-       commit = lookup_commit(sha1);
+       commit = lookup_commit(oid->hash);
        if (!commit)
                die(_("couldn't look up newly created commit"));
        if (parse_commit(commit))
@@ -1477,7 +1477,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
 
-       head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
+       head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL);
        if (!strcmp(head, "HEAD"))
                head = _("detached HEAD");
        else
@@ -1522,8 +1522,8 @@ static int git_commit_config(const char *k, const char *v, void *cb)
        return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const unsigned char *oldsha1,
-                           const unsigned char *newsha1)
+static int run_rewrite_hook(const struct object_id *oldoid,
+                           const struct object_id *newoid)
 {
        struct child_process proc = CHILD_PROCESS_INIT;
        const char *argv[3];
@@ -1544,7 +1544,7 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
        code = start_command(&proc);
        if (code)
                return code;
-       strbuf_addf(&sb, "%s %s\n", sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
+       strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
        sigchain_push(SIGPIPE, SIG_IGN);
        write_in_full(proc.in, sb.buf, sb.len);
        close(proc.in);
@@ -1636,7 +1636,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
        char *nl;
-       unsigned char sha1[20];
+       struct object_id oid;
        struct commit_list *parents = NULL;
        struct stat statbuf;
        struct commit *current_head = NULL;
@@ -1651,10 +1651,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
        s.colopts = 0;
 
-       if (get_sha1("HEAD", sha1))
+       if (get_sha1("HEAD", oid.hash))
                current_head = NULL;
        else {
-               current_head = lookup_commit_or_die(sha1, "HEAD");
+               current_head = lookup_commit_or_die(oid.hash, "HEAD");
                if (parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
@@ -1759,7 +1759,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
-                        parents, sha1, author_ident.buf, sign_commit, extra)) {
+                        parents, oid.hash, author_ident.buf, sign_commit, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
@@ -1776,7 +1776,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
-           ref_transaction_update(transaction, "HEAD", sha1,
+           ref_transaction_update(transaction, "HEAD", oid.hash,
                                   current_head
                                   ? current_head->object.oid.hash : null_sha1,
                                   0, sb.buf, &err) ||
@@ -1805,13 +1805,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                cfg = init_copy_notes_for_rewrite("amend");
                if (cfg) {
                        /* we are amending, so current_head is not NULL */
-                       copy_note_for_rewrite(cfg, current_head->object.oid.hash, sha1);
+                       copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash);
                        finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
                }
-               run_rewrite_hook(current_head->object.oid.hash, sha1);
+               run_rewrite_hook(&current_head->object.oid, &oid);
        }
        if (!quiet)
-               print_summary(prefix, sha1, !current_head);
+               print_summary(prefix, &oid, !current_head);
 
        strbuf_release(&err);
        return 0;
index a04b4f2ef337d4d9d8cb24de7288630ee8ab823c..acb05940fc3cd902d4c9bfa480f2c473dcc4d9d5 100644 (file)
@@ -53,7 +53,7 @@ static void loose_garbage(const char *path)
                report_garbage(PACKDIR_FILE_GARBAGE, path);
 }
 
-static int count_loose(const unsigned char *sha1, const char *path, void *data)
+static int count_loose(const struct object_id *oid, const char *path, void *data)
 {
        struct stat st;
 
@@ -62,7 +62,7 @@ static int count_loose(const unsigned char *sha1, const char *path, void *data)
        else {
                loose_size += on_disk_bytes(st);
                loose++;
-               if (verbose && has_sha1_pack(sha1))
+               if (verbose && has_sha1_pack(oid->hash))
                        packed_loose++;
        }
        return 0;
index 6769446e1f57537879fd1b411f83ba7393bad338..76c18059bf7ad059d4cca70712f4a782cc02d0eb 100644 (file)
@@ -40,11 +40,11 @@ static const char *diff_index_args[] = {
 
 struct commit_name {
        struct hashmap_entry entry;
-       unsigned char peeled[20];
+       struct object_id peeled;
        struct tag *tag;
        unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
        unsigned name_checked:1;
-       unsigned char sha1[20];
+       struct object_id oid;
        char *path;
 };
 
@@ -55,17 +55,17 @@ static const char *prio_names[] = {
 static int commit_name_cmp(const struct commit_name *cn1,
                const struct commit_name *cn2, const void *peeled)
 {
-       return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled);
+       return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled);
 }
 
-static inline struct commit_name *find_commit_name(const unsigned char *peeled)
+static inline struct commit_name *find_commit_name(const struct object_id *peeled)
 {
-       return hashmap_get_from_hash(&names, sha1hash(peeled), peeled);
+       return hashmap_get_from_hash(&names, sha1hash(peeled->hash), peeled->hash);
 }
 
 static int replace_name(struct commit_name *e,
                               int prio,
-                              const unsigned char *sha1,
+                              const struct object_id *oid,
                               struct tag **tag)
 {
        if (!e || e->prio < prio)
@@ -78,13 +78,13 @@ static int replace_name(struct commit_name *e,
                struct tag *t;
 
                if (!e->tag) {
-                       t = lookup_tag(e->sha1);
+                       t = lookup_tag(e->oid.hash);
                        if (!t || parse_tag(t))
                                return 1;
                        e->tag = t;
                }
 
-               t = lookup_tag(sha1);
+               t = lookup_tag(oid->hash);
                if (!t || parse_tag(t))
                        return 0;
                *tag = t;
@@ -97,24 +97,24 @@ static int replace_name(struct commit_name *e,
 }
 
 static void add_to_known_names(const char *path,
-                              const unsigned char *peeled,
+                              const struct object_id *peeled,
                               int prio,
-                              const unsigned char *sha1)
+                              const struct object_id *oid)
 {
        struct commit_name *e = find_commit_name(peeled);
        struct tag *tag = NULL;
-       if (replace_name(e, prio, sha1, &tag)) {
+       if (replace_name(e, prio, oid, &tag)) {
                if (!e) {
                        e = xmalloc(sizeof(struct commit_name));
-                       hashcpy(e->peeled, peeled);
-                       hashmap_entry_init(e, sha1hash(peeled));
+                       oidcpy(&e->peeled, peeled);
+                       hashmap_entry_init(e, sha1hash(peeled->hash));
                        hashmap_add(&names, e);
                        e->path = NULL;
                }
                e->tag = tag;
                e->prio = prio;
                e->name_checked = 0;
-               hashcpy(e->sha1, sha1);
+               oidcpy(&e->oid, oid);
                free(e->path);
                e->path = xstrdup(path);
        }
@@ -186,7 +186,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
        else
                prio = 0;
 
-       add_to_known_names(all ? path + 5 : path + 10, peeled.hash, prio, oid->hash);
+       add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
        return 0;
 }
 
@@ -244,7 +244,7 @@ static unsigned long finish_depth_computation(
 static void display_name(struct commit_name *n)
 {
        if (n->prio == 2 && !n->tag) {
-               n->tag = lookup_tag(n->sha1);
+               n->tag = lookup_tag(n->oid.hash);
                if (!n->tag || parse_tag(n->tag))
                        die(_("annotated tag %s not available"), n->path);
        }
@@ -262,14 +262,14 @@ static void display_name(struct commit_name *n)
                printf("%s", n->path);
 }
 
-static void show_suffix(int depth, const unsigned char *sha1)
+static void show_suffix(int depth, const struct object_id *oid)
 {
-       printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
+       printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
 }
 
 static void describe(const char *arg, int last_one)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        struct commit *cmit, *gave_up_on = NULL;
        struct commit_list *list;
        struct commit_name *n;
@@ -278,20 +278,20 @@ static void describe(const char *arg, int last_one)
        unsigned long seen_commits = 0;
        unsigned int unannotated_cnt = 0;
 
-       if (get_sha1(arg, sha1))
+       if (get_oid(arg, &oid))
                die(_("Not a valid object name %s"), arg);
-       cmit = lookup_commit_reference(sha1);
+       cmit = lookup_commit_reference(oid.hash);
        if (!cmit)
                die(_("%s is not a valid '%s' object"), arg, commit_type);
 
-       n = find_commit_name(cmit->object.oid.hash);
+       n = find_commit_name(&cmit->object.oid);
        if (n && (tags || all || n->prio == 2)) {
                /*
                 * Exact match to an existing ref.
                 */
                display_name(n);
                if (longformat)
-                       show_suffix(0, n->tag ? n->tag->tagged->oid.hash : sha1);
+                       show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
                if (dirty)
                        printf("%s", dirty);
                printf("\n");
@@ -308,7 +308,7 @@ static void describe(const char *arg, int last_one)
                struct commit *c;
                struct commit_name *n = hashmap_iter_first(&names, &iter);
                for (; n; n = hashmap_iter_next(&iter)) {
-                       c = lookup_commit_reference_gently(n->peeled, 1);
+                       c = lookup_commit_reference_gently(n->peeled.hash, 1);
                        if (c)
                                c->util = n;
                }
@@ -412,7 +412,7 @@ static void describe(const char *arg, int last_one)
 
        display_name(all_matches[0].name);
        if (abbrev)
-               show_suffix(all_matches[0].depth, cmit->object.oid.hash);
+               show_suffix(all_matches[0].depth, &cmit->object.oid);
        if (dirty)
                printf("%s", dirty);
        printf("\n");
index 8ce00480cd484e10b0b46d931c934fcd3ba7b71e..326f88b6576d6c02e9a0bcecb5a0b402bb257372 100644 (file)
@@ -7,46 +7,44 @@
 
 static struct rev_info log_tree_opt;
 
-static int diff_tree_commit_sha1(const unsigned char *sha1)
+static int diff_tree_commit_sha1(const struct object_id *oid)
 {
-       struct commit *commit = lookup_commit_reference(sha1);
+       struct commit *commit = lookup_commit_reference(oid->hash);
        if (!commit)
                return -1;
        return log_tree_commit(&log_tree_opt, commit);
 }
 
 /* Diff one or more commits. */
-static int stdin_diff_commit(struct commit *commit, char *line, int len)
+static int stdin_diff_commit(struct commit *commit, const char *p)
 {
-       unsigned char sha1[20];
-       if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
-               /* Graft the fake parents locally to the commit */
-               int pos = 41;
-               struct commit_list **pptr;
-
-               /* Free the real parent list */
-               free_commit_list(commit->parents);
-               commit->parents = NULL;
-               pptr = &(commit->parents);
-               while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
-                       struct commit *parent = lookup_commit(sha1);
-                       if (parent) {
-                               pptr = &commit_list_insert(parent, pptr)->next;
-                       }
-                       pos += 41;
+       struct object_id oid;
+       struct commit_list **pptr = NULL;
+
+       /* Graft the fake parents locally to the commit */
+       while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) {
+               struct commit *parent = lookup_commit(oid.hash);
+               if (!pptr) {
+                       /* Free the real parent list */
+                       free_commit_list(commit->parents);
+                       commit->parents = NULL;
+                       pptr = &(commit->parents);
+               }
+               if (parent) {
+                       pptr = &commit_list_insert(parent, pptr)->next;
                }
        }
        return log_tree_commit(&log_tree_opt, commit);
 }
 
 /* Diff two trees. */
-static int stdin_diff_trees(struct tree *tree1, char *line, int len)
+static int stdin_diff_trees(struct tree *tree1, const char *p)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        struct tree *tree2;
-       if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1))
+       if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
                return error("Need exactly two trees, separated by a space");
-       tree2 = lookup_tree(sha1);
+       tree2 = lookup_tree(oid.hash);
        if (!tree2 || parse_tree(tree2))
                return -1;
        printf("%s %s\n", oid_to_hex(&tree1->object.oid),
@@ -60,23 +58,24 @@ static int stdin_diff_trees(struct tree *tree1, char *line, int len)
 static int diff_tree_stdin(char *line)
 {
        int len = strlen(line);
-       unsigned char sha1[20];
+       struct object_id oid;
        struct object *obj;
+       const char *p;
 
        if (!len || line[len-1] != '\n')
                return -1;
        line[len-1] = 0;
-       if (get_sha1_hex(line, sha1))
+       if (parse_oid_hex(line, &oid, &p))
                return -1;
-       obj = parse_object(sha1);
+       obj = parse_object(oid.hash);
        if (!obj)
                return -1;
        if (obj->type == OBJ_COMMIT)
-               return stdin_diff_commit((struct commit *)obj, line, len);
+               return stdin_diff_commit((struct commit *)obj, p);
        if (obj->type == OBJ_TREE)
-               return stdin_diff_trees((struct tree *)obj, line, len);
+               return stdin_diff_trees((struct tree *)obj, p);
        error("Object %s is a %s, not a commit or tree",
-             sha1_to_hex(sha1), typename(obj->type));
+             oid_to_hex(&oid), typename(obj->type));
        return -1;
 }
 
@@ -141,7 +140,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                break;
        case 1:
                tree1 = opt->pending.objects[0].item;
-               diff_tree_commit_sha1(tree1->oid.hash);
+               diff_tree_commit_sha1(&tree1->oid);
                break;
        case 2:
                tree1 = opt->pending.objects[0].item;
@@ -164,9 +163,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                        opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
                                               DIFF_SETUP_USE_CACHE);
                while (fgets(line, sizeof(line), stdin)) {
-                       unsigned char sha1[20];
+                       struct object_id oid;
 
-                       if (get_sha1_hex(line, sha1)) {
+                       if (get_oid_hex(line, &oid)) {
                                fputs(line, stdout);
                                fflush(stdout);
                        }
index d13350ce832cd8c0064c8a5b2d3c9be64f876b8d..25e54ad3edb7dfd7d5e5e9f1ded4dc508048ddc6 100644 (file)
@@ -254,6 +254,49 @@ static int ensure_leading_directories(char *path)
        }
 }
 
+/*
+ * Unconditional writing of a plain regular file is what
+ * "git difftool --dir-diff" wants to do for symlinks.  We are preparing two
+ * temporary directories to be fed to a Git-unaware tool that knows how to
+ * show a diff of two directories (e.g. "diff -r A B").
+ *
+ * Because the tool is Git-unaware, if a symbolic link appears in either of
+ * these temporary directories, it will try to dereference and show the
+ * difference of the target of the symbolic link, which is not what we want,
+ * as the goal of the dir-diff mode is to produce an output that is logically
+ * equivalent to what "git diff" produces.
+ *
+ * Most importantly, we want to get textual comparison of the result of the
+ * readlink(2).  get_symlink() provides that---it returns the contents of
+ * the symlink that gets written to a regular file to force the external tool
+ * to compare the readlink(2) result as text, even on a filesystem that is
+ * capable of doing a symbolic link.
+ */
+static char *get_symlink(const struct object_id *oid, const char *path)
+{
+       char *data;
+       if (is_null_oid(oid)) {
+               /* The symlink is unknown to Git so read from the filesystem */
+               struct strbuf link = STRBUF_INIT;
+               if (has_symlinks) {
+                       if (strbuf_readlink(&link, path, strlen(path)))
+                               die(_("could not read symlink %s"), path);
+               } else if (strbuf_read_file(&link, path, 128))
+                       die(_("could not read symlink file %s"), path);
+
+               data = strbuf_detach(&link, NULL);
+       } else {
+               enum object_type type;
+               unsigned long size;
+               data = read_sha1_file(oid->hash, &type, &size);
+               if (!data)
+                       die(_("could not read object %s for symlink %s"),
+                               oid_to_hex(oid), path);
+       }
+
+       return data;
+}
+
 static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                        int argc, const char **argv)
 {
@@ -270,8 +313,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        struct hashmap working_tree_dups, submodules, symlinks2;
        struct hashmap_iter iter;
        struct pair_entry *entry;
-       enum object_type type;
-       unsigned long size;
        struct index_state wtindex;
        struct checkout lstate, rstate;
        int rc, flags = RUN_GIT_CMD, err = 0;
@@ -377,13 +418,13 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                }
 
                if (S_ISLNK(lmode)) {
-                       char *content = read_sha1_file(loid.hash, &type, &size);
+                       char *content = get_symlink(&loid, src_path);
                        add_left_or_right(&symlinks2, src_path, content, 0);
                        free(content);
                }
 
                if (S_ISLNK(rmode)) {
-                       char *content = read_sha1_file(roid.hash, &type, &size);
+                       char *content = get_symlink(&roid, dst_path);
                        add_left_or_right(&symlinks2, dst_path, content, 1);
                        free(content);
                }
@@ -397,7 +438,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                                return error("could not write '%s'", src_path);
                }
 
-               if (rmode) {
+               if (rmode && !S_ISLNK(rmode)) {
                        struct working_tree_entry *entry;
 
                        /* Avoid duplicate working_tree entries */
index 1e815b5577cc05a6133ded56dda4b84596f80514..e0220630d00cf2c069ce1a43e9511df57685acd2 100644 (file)
@@ -212,7 +212,7 @@ static char *anonymize_blob(unsigned long *size)
        return strbuf_detach(&out, NULL);
 }
 
-static void export_blob(const unsigned char *sha1)
+static void export_blob(const struct object_id *oid)
 {
        unsigned long size;
        enum object_type type;
@@ -223,34 +223,34 @@ static void export_blob(const unsigned char *sha1)
        if (no_data)
                return;
 
-       if (is_null_sha1(sha1))
+       if (is_null_oid(oid))
                return;
 
-       object = lookup_object(sha1);
+       object = lookup_object(oid->hash);
        if (object && object->flags & SHOWN)
                return;
 
        if (anonymize) {
                buf = anonymize_blob(&size);
-               object = (struct object *)lookup_blob(sha1);
+               object = (struct object *)lookup_blob(oid->hash);
                eaten = 0;
        } else {
-               buf = read_sha1_file(sha1, &type, &size);
+               buf = read_sha1_file(oid->hash, &type, &size);
                if (!buf)
-                       die ("Could not read blob %s", sha1_to_hex(sha1));
-               if (check_sha1_signature(sha1, buf, size, typename(type)) < 0)
-                       die("sha1 mismatch in blob %s", sha1_to_hex(sha1));
-               object = parse_object_buffer(sha1, type, size, buf, &eaten);
+                       die ("Could not read blob %s", oid_to_hex(oid));
+               if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0)
+                       die("sha1 mismatch in blob %s", oid_to_hex(oid));
+               object = parse_object_buffer(oid->hash, type, size, buf, &eaten);
        }
 
        if (!object)
-               die("Could not read blob %s", sha1_to_hex(sha1));
+               die("Could not read blob %s", oid_to_hex(oid));
 
        mark_next_object(object);
 
        printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size);
        if (size && fwrite(buf, size, 1, stdout) != 1)
-               die_errno ("Could not write blob '%s'", sha1_to_hex(sha1));
+               die_errno ("Could not write blob '%s'", oid_to_hex(oid));
        printf("\n");
 
        show_progress();
@@ -323,19 +323,19 @@ static void print_path(const char *path)
        }
 }
 
-static void *generate_fake_sha1(const void *old, size_t *len)
+static void *generate_fake_oid(const void *old, size_t *len)
 {
        static uint32_t counter = 1; /* avoid null sha1 */
-       unsigned char *out = xcalloc(20, 1);
-       put_be32(out + 16, counter++);
+       unsigned char *out = xcalloc(GIT_SHA1_RAWSZ, 1);
+       put_be32(out + GIT_SHA1_RAWSZ - 4, counter++);
        return out;
 }
 
-static const unsigned char *anonymize_sha1(const unsigned char *sha1)
+static const unsigned char *anonymize_sha1(const struct object_id *oid)
 {
        static struct hashmap sha1s;
-       size_t len = 20;
-       return anonymize_mem(&sha1s, generate_fake_sha1, sha1, &len);
+       size_t len = GIT_SHA1_RAWSZ;
+       return anonymize_mem(&sha1s, generate_fake_oid, oid, &len);
 }
 
 static void show_filemodify(struct diff_queue_struct *q,
@@ -383,7 +383,7 @@ static void show_filemodify(struct diff_queue_struct *q,
                        if (no_data || S_ISGITLINK(spec->mode))
                                printf("M %06o %s ", spec->mode,
                                       sha1_to_hex(anonymize ?
-                                                  anonymize_sha1(spec->oid.hash) :
+                                                  anonymize_sha1(&spec->oid) :
                                                   spec->oid.hash));
                        else {
                                struct object *object = lookup_object(spec->oid.hash);
@@ -572,7 +572,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        /* Export the referenced blobs, and remember the marks. */
        for (i = 0; i < diff_queued_diff.nr; i++)
                if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
-                       export_blob(diff_queued_diff.queue[i]->two->oid.hash);
+                       export_blob(&diff_queued_diff.queue[i]->two->oid);
 
        refname = commit->util;
        if (anonymize) {
@@ -797,14 +797,14 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
 
        for (i = 0; i < info->nr; i++) {
                struct rev_cmdline_entry *e = info->rev + i;
-               unsigned char sha1[20];
+               struct object_id oid;
                struct commit *commit;
                char *full_name;
 
                if (e->flags & UNINTERESTING)
                        continue;
 
-               if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
+               if (dwim_ref(e->name, strlen(e->name), oid.hash, &full_name) != 1)
                        continue;
 
                if (refspecs) {
@@ -828,7 +828,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                case OBJ_COMMIT:
                        break;
                case OBJ_BLOB:
-                       export_blob(commit->object.oid.hash);
+                       export_blob(&commit->object.oid);
                        continue;
                default: /* OBJ_TAG (nested tags) is already handled */
                        warning("Tag points to object of unexpected type %s, skipping.",
@@ -912,7 +912,7 @@ static void import_marks(char *input_file)
        while (fgets(line, sizeof(line), f)) {
                uint32_t mark;
                char *line_end, *mark_end;
-               unsigned char sha1[20];
+               struct object_id oid;
                struct object *object;
                struct commit *commit;
                enum object_type type;
@@ -924,28 +924,28 @@ static void import_marks(char *input_file)
 
                mark = strtoumax(line + 1, &mark_end, 10);
                if (!mark || mark_end == line + 1
-                       || *mark_end != ' ' || get_sha1_hex(mark_end + 1, sha1))
+                       || *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid))
                        die("corrupt mark line: %s", line);
 
                if (last_idnum < mark)
                        last_idnum = mark;
 
-               type = sha1_object_info(sha1, NULL);
+               type = sha1_object_info(oid.hash, NULL);
                if (type < 0)
-                       die("object not found: %s", sha1_to_hex(sha1));
+                       die("object not found: %s", oid_to_hex(&oid));
 
                if (type != OBJ_COMMIT)
                        /* only commits */
                        continue;
 
-               commit = lookup_commit(sha1);
+               commit = lookup_commit(oid.hash);
                if (!commit)
-                       die("not a commit? can't happen: %s", sha1_to_hex(sha1));
+                       die("not a commit? can't happen: %s", oid_to_hex(&oid));
 
                object = &commit->object;
 
                if (object->flags & SHOWN)
-                       error("Object %s already has a mark", sha1_to_hex(sha1));
+                       error("Object %s already has a mark", oid_to_hex(&oid));
 
                mark_object(object, mark);
 
index cfe9e447c27469407ab439bacb540a4d0b68d4b4..2a1c1c213f4092a274c562324c4d1820c58a6be2 100644 (file)
@@ -219,12 +219,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
         * remote no-such-ref' would silently succeed without issuing
         * an error.
         */
-       for (i = 0; i < nr_sought; i++) {
-               if (!sought[i] || sought[i]->matched)
-                       continue;
-               error("no such remote ref %s", sought[i]->name);
-               ret = 1;
-       }
+       ret |= report_unmatched_refs(sought, nr_sought);
 
        while (ref) {
                printf("%s %s\n",
index efab62fd85e314ccb6ec5becedead91823d03dde..6faa3c0d2424600a5e69dd20466aa763d95b586c 100644 (file)
@@ -41,7 +41,7 @@ struct src_data {
 };
 
 struct origin_data {
-       unsigned char sha1[20];
+       struct object_id oid;
        unsigned is_local_branch:1;
 };
 
@@ -59,8 +59,8 @@ static struct string_list origins = STRING_LIST_INIT_DUP;
 struct merge_parents {
        int alloc, nr;
        struct merge_parent {
-               unsigned char given[20];
-               unsigned char commit[20];
+               struct object_id given;
+               struct object_id commit;
                unsigned char used;
        } *item;
 };
@@ -70,14 +70,14 @@ struct merge_parents {
  * hundreds of heads at a time anyway.
  */
 static struct merge_parent *find_merge_parent(struct merge_parents *table,
-                                             unsigned char *given,
-                                             unsigned char *commit)
+                                             struct object_id *given,
+                                             struct object_id *commit)
 {
        int i;
        for (i = 0; i < table->nr; i++) {
-               if (given && hashcmp(table->item[i].given, given))
+               if (given && oidcmp(&table->item[i].given, given))
                        continue;
-               if (commit && hashcmp(table->item[i].commit, commit))
+               if (commit && oidcmp(&table->item[i].commit, commit))
                        continue;
                return &table->item[i];
        }
@@ -85,14 +85,14 @@ static struct merge_parent *find_merge_parent(struct merge_parents *table,
 }
 
 static void add_merge_parent(struct merge_parents *table,
-                            unsigned char *given,
-                            unsigned char *commit)
+                            struct object_id *given,
+                            struct object_id *commit)
 {
        if (table->nr && find_merge_parent(table, given, commit))
                return;
        ALLOC_GROW(table->item, table->nr + 1, table->alloc);
-       hashcpy(table->item[table->nr].given, given);
-       hashcpy(table->item[table->nr].commit, commit);
+       oidcpy(&table->item[table->nr].given, given);
+       oidcpy(&table->item[table->nr].commit, commit);
        table->item[table->nr].used = 0;
        table->nr++;
 }
@@ -106,30 +106,30 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
        struct src_data *src_data;
        struct string_list_item *item;
        int pulling_head = 0;
-       unsigned char sha1[20];
+       struct object_id oid;
 
-       if (len < 43 || line[40] != '\t')
+       if (len < GIT_SHA1_HEXSZ + 3 || line[GIT_SHA1_HEXSZ] != '\t')
                return 1;
 
-       if (starts_with(line + 41, "not-for-merge"))
+       if (starts_with(line + GIT_SHA1_HEXSZ + 1, "not-for-merge"))
                return 0;
 
-       if (line[41] != '\t')
+       if (line[GIT_SHA1_HEXSZ + 1] != '\t')
                return 2;
 
-       i = get_sha1_hex(line, sha1);
+       i = get_oid_hex(line, &oid);
        if (i)
                return 3;
 
-       if (!find_merge_parent(merge_parents, sha1, NULL))
+       if (!find_merge_parent(merge_parents, &oid, NULL))
                return 0; /* subsumed by other parents */
 
        origin_data = xcalloc(1, sizeof(struct origin_data));
-       hashcpy(origin_data->sha1, sha1);
+       oidcpy(&origin_data->oid, &oid);
 
        if (line[len - 1] == '\n')
                line[len - 1] = 0;
-       line += 42;
+       line += GIT_SHA1_HEXSZ + 2;
 
        /*
         * At this point, line points at the beginning of comment e.g.
@@ -338,10 +338,10 @@ static void shortlog(const char *name,
        struct string_list committers = STRING_LIST_INIT_DUP;
        int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
        struct strbuf sb = STRBUF_INIT;
-       const unsigned char *sha1 = origin_data->sha1;
+       const struct object_id *oid = &origin_data->oid;
        int limit = opts->shortlog_len;
 
-       branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
+       branch = deref_tag(parse_object(oid->hash), oid_to_hex(oid), GIT_SHA1_HEXSZ);
        if (!branch || branch->type != OBJ_COMMIT)
                return;
 
@@ -531,7 +531,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
 }
 
 static void find_merge_parents(struct merge_parents *result,
-                              struct strbuf *in, unsigned char *head)
+                              struct strbuf *in, struct object_id *head)
 {
        struct commit_list *parents;
        struct commit *head_commit;
@@ -542,31 +542,31 @@ static void find_merge_parents(struct merge_parents *result,
                int len;
                char *p = in->buf + pos;
                char *newline = strchr(p, '\n');
-               unsigned char sha1[20];
+               struct object_id oid;
                struct commit *parent;
                struct object *obj;
 
                len = newline ? newline - p : strlen(p);
                pos += len + !!newline;
 
-               if (len < 43 ||
-                   get_sha1_hex(p, sha1) ||
-                   p[40] != '\t' ||
-                   p[41] != '\t')
+               if (len < GIT_SHA1_HEXSZ + 3 ||
+                   get_oid_hex(p, &oid) ||
+                   p[GIT_SHA1_HEXSZ] != '\t' ||
+                   p[GIT_SHA1_HEXSZ + 1] != '\t')
                        continue; /* skip not-for-merge */
                /*
                 * Do not use get_merge_parent() here; we do not have
                 * "name" here and we do not want to contaminate its
                 * util field yet.
                 */
-               obj = parse_object(sha1);
+               obj = parse_object(oid.hash);
                parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
                if (!parent)
                        continue;
                commit_list_insert(parent, &parents);
-               add_merge_parent(result, obj->oid.hash, parent->object.oid.hash);
+               add_merge_parent(result, &obj->oid, &parent->object.oid);
        }
-       head_commit = lookup_commit(head);
+       head_commit = lookup_commit(head->hash);
        if (head_commit)
                commit_list_insert(head_commit, &parents);
        parents = reduce_heads(parents);
@@ -574,7 +574,7 @@ static void find_merge_parents(struct merge_parents *result,
        while (parents) {
                struct commit *cmit = pop_commit(&parents);
                for (i = 0; i < result->nr; i++)
-                       if (!hashcmp(result->item[i].commit, cmit->object.oid.hash))
+                       if (!oidcmp(&result->item[i].commit, &cmit->object.oid))
                                result->item[i].used = 1;
        }
 
@@ -592,7 +592,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                  struct fmt_merge_msg_opts *opts)
 {
        int i = 0, pos = 0;
-       unsigned char head_sha1[20];
+       struct object_id head_oid;
        const char *current_branch;
        void *current_branch_to_free;
        struct merge_parents merge_parents;
@@ -601,13 +601,13 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 
        /* get current branch */
        current_branch = current_branch_to_free =
-               resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
+               resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL);
        if (!current_branch)
                die("No current branch");
        if (starts_with(current_branch, "refs/heads/"))
                current_branch += 11;
 
-       find_merge_parents(&merge_parents, in, head_sha1);
+       find_merge_parents(&merge_parents, in, &head_oid);
 
        /* get a line */
        while (pos < in->len) {
@@ -633,7 +633,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                struct commit *head;
                struct rev_info rev;
 
-               head = lookup_commit_or_die(head_sha1, "HEAD");
+               head = lookup_commit_or_die(head_oid.hash, "HEAD");
                init_revisions(&rev, NULL);
                rev.commit_format = CMIT_FMT_ONELINE;
                rev.ignore_merges = 1;
index 1a5caccd0f5ee3fa56af06e8f536271f58312665..f76e4163abbac825c00111504a040f8091d967d3 100644 (file)
@@ -396,13 +396,13 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 
 static int default_refs;
 
-static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
+static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
        unsigned long timestamp)
 {
        struct object *obj;
 
-       if (!is_null_sha1(sha1)) {
-               obj = lookup_object(sha1);
+       if (!is_null_oid(oid)) {
+               obj = lookup_object(oid->hash);
                if (obj && (obj->flags & HAS_OBJ)) {
                        if (timestamp && name_objects)
                                add_decoration(fsck_walk_options.object_names,
@@ -411,13 +411,13 @@ static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
                        obj->used = 1;
                        mark_object_reachable(obj);
                } else {
-                       error("%s: invalid reflog entry %s", refname, sha1_to_hex(sha1));
+                       error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
        }
 }
 
-static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
@@ -425,10 +425,10 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 
        if (verbose)
                fprintf(stderr, "Checking reflog %s->%s\n",
-                       sha1_to_hex(osha1), sha1_to_hex(nsha1));
+                       oid_to_hex(ooid), oid_to_hex(noid));
 
-       fsck_handle_reflog_sha1(refname, osha1, 0);
-       fsck_handle_reflog_sha1(refname, nsha1, timestamp);
+       fsck_handle_reflog_oid(refname, ooid, 0);
+       fsck_handle_reflog_oid(refname, noid, timestamp);
        return 0;
 }
 
@@ -491,7 +491,7 @@ static void get_default_heads(void)
        }
 }
 
-static struct object *parse_loose_object(const unsigned char *sha1,
+static struct object *parse_loose_object(const struct object_id *oid,
                                         const char *path)
 {
        struct object *obj;
@@ -500,27 +500,27 @@ static struct object *parse_loose_object(const unsigned char *sha1,
        unsigned long size;
        int eaten;
 
-       if (read_loose_object(path, sha1, &type, &size, &contents) < 0)
+       if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0)
                return NULL;
 
        if (!contents && type != OBJ_BLOB)
                die("BUG: read_loose_object streamed a non-blob");
 
-       obj = parse_object_buffer(sha1, type, size, contents, &eaten);
+       obj = parse_object_buffer(oid->hash, type, size, contents, &eaten);
 
        if (!eaten)
                free(contents);
        return obj;
 }
 
-static int fsck_loose(const unsigned char *sha1, const char *path, void *data)
+static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 {
-       struct object *obj = parse_loose_object(sha1, path);
+       struct object *obj = parse_loose_object(oid, path);
 
        if (!obj) {
                errors_found |= ERROR_OBJECT;
                error("%s: object corrupt or missing: %s",
-                     sha1_to_hex(sha1), path);
+                     oid_to_hex(oid), path);
                return 0; /* keep checking other objects */
        }
 
@@ -619,26 +619,26 @@ static int fsck_cache_tree(struct cache_tree *it)
        return err;
 }
 
-static void mark_object_for_connectivity(const unsigned char *sha1)
+static void mark_object_for_connectivity(const struct object_id *oid)
 {
-       struct object *obj = lookup_unknown_object(sha1);
+       struct object *obj = lookup_unknown_object(oid->hash);
        obj->flags |= HAS_OBJ;
 }
 
-static int mark_loose_for_connectivity(const unsigned char *sha1,
+static int mark_loose_for_connectivity(const struct object_id *oid,
                                       const char *path,
                                       void *data)
 {
-       mark_object_for_connectivity(sha1);
+       mark_object_for_connectivity(oid);
        return 0;
 }
 
-static int mark_packed_for_connectivity(const unsigned char *sha1,
+static int mark_packed_for_connectivity(const struct object_id *oid,
                                        struct packed_git *pack,
                                        uint32_t pos,
                                        void *data)
 {
-       mark_object_for_connectivity(sha1);
+       mark_object_for_connectivity(oid);
        return 0;
 }
 
index a2b9e8924e1a3e3a537d86c4a3da1fa1967c1863..c2c61a57bb359a37373ffd49aba79e5d5514a9fc 100644 (file)
@@ -64,17 +64,6 @@ static void report_pack_garbage(unsigned seen_bits, const char *path)
                string_list_append(&pack_garbage, path);
 }
 
-static void git_config_date_string(const char *key, const char **output)
-{
-       if (git_config_get_string_const(key, output))
-               return;
-       if (strcmp(*output, "now")) {
-               unsigned long now = approxidate("now");
-               if (approxidate(*output) >= now)
-                       git_die_config(key, _("Invalid %s: '%s'"), key, *output);
-       }
-}
-
 static void process_log_file(void)
 {
        struct stat st;
@@ -131,9 +120,9 @@ static void gc_config(void)
        git_config_get_int("gc.auto", &gc_auto_threshold);
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
        git_config_get_bool("gc.autodetach", &detach_auto);
-       git_config_date_string("gc.pruneexpire", &prune_expire);
-       git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
-       git_config_date_string("gc.logexpiry", &gc_log_expire);
+       git_config_get_expiry("gc.pruneexpire", &prune_expire);
+       git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
+       git_config_get_expiry("gc.logexpiry", &gc_log_expire);
 
        git_config(git_default_config, NULL);
 }
index 9304c33e75051baa65ee02d1daa03c361e25339e..837836fb3e3bf9cdeb25edf8789428aaf813f49d 100644 (file)
@@ -294,17 +294,17 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)
        return st;
 }
 
-static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+static void *lock_and_read_oid_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
 {
        void *data;
 
        grep_read_lock();
-       data = read_sha1_file(sha1, type, size);
+       data = read_sha1_file(oid->hash, type, size);
        grep_read_unlock();
        return data;
 }
 
-static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
+static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
                     const char *filename, int tree_name_len,
                     const char *path)
 {
@@ -323,7 +323,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
 
 #ifndef NO_PTHREADS
        if (num_threads) {
-               add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
+               add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
                strbuf_release(&pathbuf);
                return 0;
        } else
@@ -332,7 +332,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
                struct grep_source gs;
                int hit;
 
-               grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
+               grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
                strbuf_release(&pathbuf);
                hit = grep_source(opt, &gs);
 
@@ -690,7 +690,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
                            ce_skip_worktree(ce)) {
                                if (ce_stage(ce) || ce_intent_to_add(ce))
                                        continue;
-                               hit |= grep_sha1(opt, ce->oid.hash, ce->name,
+                               hit |= grep_oid(opt, &ce->oid, ce->name,
                                                 0, ce->name);
                        } else {
                                hit |= grep_file(opt, ce->name);
@@ -750,7 +750,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                strbuf_add(base, entry.path, te_len);
 
                if (S_ISREG(entry.mode)) {
-                       hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
+                       hit |= grep_oid(opt, entry.oid, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
                } else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
@@ -758,7 +758,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        void *data;
                        unsigned long size;
 
-                       data = lock_and_read_sha1_file(entry.oid->hash, &type, &size);
+                       data = lock_and_read_oid_file(entry.oid, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
                                    oid_to_hex(entry.oid));
@@ -787,7 +787,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                       struct object *obj, const char *name, const char *path)
 {
        if (obj->type == OBJ_BLOB)
-               return grep_sha1(opt, obj->oid.hash, name, 0, path);
+               return grep_oid(opt, &obj->oid, name, 0, path);
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
@@ -1169,7 +1169,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        allow_revs = use_index && !untracked;
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
-               unsigned char sha1[20];
+               struct object_id oid;
                struct object_context oc;
                struct object *object;
 
@@ -1184,13 +1184,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        break;
                }
 
-               if (get_sha1_with_context(arg, 0, sha1, &oc)) {
+               if (get_sha1_with_context(arg, 0, oid.hash, &oc)) {
                        if (seen_dashdash)
                                die(_("unable to resolve revision: %s"), arg);
                        break;
                }
 
-               object = parse_object_or_die(sha1, arg);
+               object = parse_object_or_die(oid.hash, arg);
                if (!seen_dashdash)
                        verify_non_filename(prefix, arg);
                add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
index f4b87c6c9f901e5834ce9e50db089fb487c6eafc..88d205f858e93f6d7c8fca17803fc6fadee47750 100644 (file)
@@ -1386,7 +1386,9 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                  unsigned char *sha1)
 {
        const char *report = "pack";
-       char name[PATH_MAX];
+       struct strbuf pack_name = STRBUF_INIT;
+       struct strbuf index_name = STRBUF_INIT;
+       struct strbuf keep_name_buf = STRBUF_INIT;
        int err;
 
        if (!from_stdin) {
@@ -1402,14 +1404,13 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                int keep_fd, keep_msg_len = strlen(keep_msg);
 
                if (!keep_name)
-                       keep_fd = odb_pack_keep(name, sizeof(name), sha1);
-               else
-                       keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+                       keep_name = odb_pack_name(&keep_name_buf, sha1, "keep");
 
+               keep_fd = odb_pack_keep(keep_name);
                if (keep_fd < 0) {
                        if (errno != EEXIST)
                                die_errno(_("cannot write keep file '%s'"),
-                                         keep_name ? keep_name : name);
+                                         keep_name);
                } else {
                        if (keep_msg_len > 0) {
                                write_or_die(keep_fd, keep_msg, keep_msg_len);
@@ -1417,28 +1418,22 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        }
                        if (close(keep_fd) != 0)
                                die_errno(_("cannot close written keep file '%s'"),
-                                         keep_name ? keep_name : name);
+                                         keep_name);
                        report = "keep";
                }
        }
 
        if (final_pack_name != curr_pack_name) {
-               if (!final_pack_name) {
-                       snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
-                                get_object_directory(), sha1_to_hex(sha1));
-                       final_pack_name = name;
-               }
+               if (!final_pack_name)
+                       final_pack_name = odb_pack_name(&pack_name, sha1, "pack");
                if (finalize_object_file(curr_pack_name, final_pack_name))
                        die(_("cannot store pack file"));
        } else if (from_stdin)
                chmod(final_pack_name, 0444);
 
        if (final_index_name != curr_index_name) {
-               if (!final_index_name) {
-                       snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
-                                get_object_directory(), sha1_to_hex(sha1));
-                       final_index_name = name;
-               }
+               if (!final_index_name)
+                       final_index_name = odb_pack_name(&index_name, sha1, "idx");
                if (finalize_object_file(curr_index_name, final_index_name))
                        die(_("cannot store index file"));
        } else
@@ -1464,6 +1459,10 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        input_offset += err;
                }
        }
+
+       strbuf_release(&index_name);
+       strbuf_release(&pack_name);
+       strbuf_release(&keep_name_buf);
 }
 
 static int git_index_pack_config(const char *k, const char *v, void *cb)
index b572a37c2611b2a8c20b1db7edac68623d371a38..cfe2a796f85ecf8dfbbb35d3232893929670b492 100644 (file)
@@ -36,12 +36,12 @@ static const char * const merge_base_usage[] = {
 
 static struct commit *get_commit_reference(const char *arg)
 {
-       unsigned char revkey[20];
+       struct object_id revkey;
        struct commit *r;
 
-       if (get_sha1(arg, revkey))
+       if (get_oid(arg, &revkey))
                die("Not a valid object name %s", arg);
-       r = lookup_commit_reference(revkey);
+       r = lookup_commit_reference(revkey.hash);
        if (!r)
                die("Not a valid commit name %s", arg);
 
@@ -113,14 +113,14 @@ struct rev_collect {
        unsigned int initial : 1;
 };
 
-static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
+static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 {
        struct commit *commit;
 
-       if (is_null_sha1(sha1))
+       if (is_null_oid(oid))
                return;
 
-       commit = lookup_commit(sha1);
+       commit = lookup_commit(oid->hash);
        if (!commit ||
            (commit->object.flags & TMP_MARK) ||
            parse_commit(commit))
@@ -131,7 +131,7 @@ static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
        commit->object.flags |= TMP_MARK;
 }
 
-static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
                                  const char *ident, unsigned long timestamp,
                                  int tz, const char *message, void *cbdata)
 {
@@ -139,15 +139,15 @@ static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 
        if (revs->initial) {
                revs->initial = 0;
-               add_one_commit(osha1, revs);
+               add_one_commit(ooid, revs);
        }
-       add_one_commit(nsha1, revs);
+       add_one_commit(noid, revs);
        return 0;
 }
 
 static int handle_fork_point(int argc, const char **argv)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        char *refname;
        const char *commitname;
        struct rev_collect revs;
@@ -155,7 +155,7 @@ static int handle_fork_point(int argc, const char **argv)
        struct commit_list *bases;
        int i, ret = 0;
 
-       switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) {
+       switch (dwim_ref(argv[0], strlen(argv[0]), oid.hash, &refname)) {
        case 0:
                die("No such ref: '%s'", argv[0]);
        case 1:
@@ -165,16 +165,16 @@ static int handle_fork_point(int argc, const char **argv)
        }
 
        commitname = (argc == 2) ? argv[1] : "HEAD";
-       if (get_sha1(commitname, sha1))
+       if (get_oid(commitname, &oid))
                die("Not a valid object name: '%s'", commitname);
 
-       derived = lookup_commit_reference(sha1);
+       derived = lookup_commit_reference(oid.hash);
        memset(&revs, 0, sizeof(revs));
        revs.initial = 1;
        for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
 
-       if (!revs.nr && !get_sha1(refname, sha1))
-               add_one_commit(sha1, &revs);
+       if (!revs.nr && !get_oid(refname, &oid))
+               add_one_commit(&oid, &revs);
 
        for (i = 0; i < revs.nr; i++)
                revs.commit[i]->object.flags &= ~TMP_MARK;
index a96d4fb501bf1441b52a313313b8c04f3187e4d9..7554b8d4127ada8261d1f913c6215e93ca42ef7d 100644 (file)
@@ -244,7 +244,7 @@ static void drop_save(void)
        unlink(git_path_merge_mode());
 }
 
-static int save_state(unsigned char *stash)
+static int save_state(struct object_id *stash)
 {
        int len;
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -265,7 +265,7 @@ static int save_state(unsigned char *stash)
        else if (!len)          /* no changes */
                return -1;
        strbuf_setlen(&buffer, buffer.len-1);
-       if (get_sha1(buffer.buf, stash))
+       if (get_oid(buffer.buf, stash))
                die(_("not a valid object: %s"), buffer.buf);
        return 0;
 }
@@ -305,18 +305,18 @@ static void reset_hard(unsigned const char *sha1, int verbose)
                die(_("read-tree failed"));
 }
 
-static void restore_state(const unsigned char *head,
-                         const unsigned char *stash)
+static void restore_state(const struct object_id *head,
+                         const struct object_id *stash)
 {
        struct strbuf sb = STRBUF_INIT;
        const char *args[] = { "stash", "apply", NULL, NULL };
 
-       if (is_null_sha1(stash))
+       if (is_null_oid(stash))
                return;
 
-       reset_hard(head, 1);
+       reset_hard(head->hash, 1);
 
-       args[2] = sha1_to_hex(stash);
+       args[2] = oid_to_hex(stash);
 
        /*
         * It is OK to ignore error here, for example when there was
@@ -376,10 +376,10 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 
 static void finish(struct commit *head_commit,
                   struct commit_list *remoteheads,
-                  const unsigned char *new_head, const char *msg)
+                  const struct object_id *new_head, const char *msg)
 {
        struct strbuf reflog_message = STRBUF_INIT;
-       const unsigned char *head = head_commit->object.oid.hash;
+       const struct object_id *head = &head_commit->object.oid;
 
        if (!msg)
                strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
@@ -397,7 +397,7 @@ static void finish(struct commit *head_commit,
                else {
                        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
                        update_ref(reflog_message.buf, "HEAD",
-                               new_head, head, 0,
+                               new_head->hash, head->hash, 0,
                                UPDATE_REFS_DIE_ON_ERR);
                        /*
                         * We ignore errors in 'gc --auto', since the
@@ -416,7 +416,7 @@ static void finish(struct commit *head_commit,
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
                diff_setup_done(&opts);
-               diff_tree_sha1(head, new_head, "", &opts);
+               diff_tree_sha1(head->hash, new_head->hash, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
        }
@@ -431,35 +431,35 @@ static void finish(struct commit *head_commit,
 static void merge_name(const char *remote, struct strbuf *msg)
 {
        struct commit *remote_head;
-       unsigned char branch_head[20];
+       struct object_id branch_head;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf bname = STRBUF_INIT;
        const char *ptr;
        char *found_ref;
        int len, early;
 
-       strbuf_branchname(&bname, remote);
+       strbuf_branchname(&bname, remote, 0);
        remote = bname.buf;
 
-       memset(branch_head, 0, sizeof(branch_head));
+       oidclr(&branch_head);
        remote_head = get_merge_parent(remote);
        if (!remote_head)
                die(_("'%s' does not point to a commit"), remote);
 
-       if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
+       if (dwim_ref(remote, strlen(remote), branch_head.hash, &found_ref) > 0) {
                if (starts_with(found_ref, "refs/heads/")) {
                        strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
-                                   sha1_to_hex(branch_head), remote);
+                                   oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
                if (starts_with(found_ref, "refs/tags/")) {
                        strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
-                                   sha1_to_hex(branch_head), remote);
+                                   oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
                if (starts_with(found_ref, "refs/remotes/")) {
                        strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
-                                   sha1_to_hex(branch_head), remote);
+                                   oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
        }
@@ -590,8 +590,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
        return git_diff_ui_config(k, v, cb);
 }
 
-static int read_tree_trivial(unsigned char *common, unsigned char *head,
-                            unsigned char *one)
+static int read_tree_trivial(struct object_id *common, struct object_id *head,
+                            struct object_id *one)
 {
        int i, nr_trees = 0;
        struct tree *trees[MAX_UNPACK_TREES];
@@ -606,13 +606,13 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head,
        opts.verbose_update = 1;
        opts.trivial_merges_only = 1;
        opts.merge = 1;
-       trees[nr_trees] = parse_tree_indirect(common);
+       trees[nr_trees] = parse_tree_indirect(common->hash);
        if (!trees[nr_trees++])
                return -1;
-       trees[nr_trees] = parse_tree_indirect(head);
+       trees[nr_trees] = parse_tree_indirect(head->hash);
        if (!trees[nr_trees++])
                return -1;
-       trees[nr_trees] = parse_tree_indirect(one);
+       trees[nr_trees] = parse_tree_indirect(one->hash);
        if (!trees[nr_trees++])
                return -1;
        opts.fn = threeway_merge;
@@ -626,9 +626,9 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head,
        return 0;
 }
 
-static void write_tree_trivial(unsigned char *sha1)
+static void write_tree_trivial(struct object_id *oid)
 {
-       if (write_cache_as_tree(sha1, 0, NULL))
+       if (write_cache_as_tree(oid->hash, 0, NULL))
                die(_("git write-tree failed to write a tree"));
 }
 
@@ -781,7 +781,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
 
 static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 {
-       unsigned char result_tree[20], result_commit[20];
+       struct object_id result_tree, result_commit;
        struct commit_list *parents, **pptr = &parents;
        static struct lock_file lock;
 
@@ -792,15 +792,15 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
                return error(_("Unable to write index."));
        rollback_lock_file(&lock);
 
-       write_tree_trivial(result_tree);
+       write_tree_trivial(&result_tree);
        printf(_("Wonderful.\n"));
        pptr = commit_list_append(head, pptr);
        pptr = commit_list_append(remoteheads->item, pptr);
        prepare_to_commit(remoteheads);
-       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
-                       result_commit, NULL, sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree.hash, parents,
+                       result_commit.hash, NULL, sign_commit))
                die(_("failed to write commit object"));
-       finish(head, remoteheads, result_commit, "In-index merge");
+       finish(head, remoteheads, &result_commit, "In-index merge");
        drop_save();
        return 0;
 }
@@ -809,12 +809,12 @@ static int finish_automerge(struct commit *head,
                            int head_subsumed,
                            struct commit_list *common,
                            struct commit_list *remoteheads,
-                           unsigned char *result_tree,
+                           struct object_id *result_tree,
                            const char *wt_strategy)
 {
        struct commit_list *parents = NULL;
        struct strbuf buf = STRBUF_INIT;
-       unsigned char result_commit[20];
+       struct object_id result_commit;
 
        free_commit_list(common);
        parents = remoteheads;
@@ -822,11 +822,11 @@ static int finish_automerge(struct commit *head,
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
-       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
-                       result_commit, NULL, sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree->hash, parents,
+                       result_commit.hash, NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
-       finish(head, remoteheads, result_commit, buf.buf);
+       finish(head, remoteheads, &result_commit, buf.buf);
        strbuf_release(&buf);
        drop_save();
        return 0;
@@ -854,18 +854,18 @@ static int suggest_conflicts(void)
 }
 
 static struct commit *is_old_style_invocation(int argc, const char **argv,
-                                             const unsigned char *head)
+                                             const struct object_id *head)
 {
        struct commit *second_token = NULL;
        if (argc > 2) {
-               unsigned char second_sha1[20];
+               struct object_id second_oid;
 
-               if (get_sha1(argv[1], second_sha1))
+               if (get_oid(argv[1], &second_oid))
                        return NULL;
-               second_token = lookup_commit_reference_gently(second_sha1, 0);
+               second_token = lookup_commit_reference_gently(second_oid.hash, 0);
                if (!second_token)
                        die(_("'%s' is not a commit"), argv[1]);
-               if (hashcmp(second_token->object.oid.hash, head))
+               if (oidcmp(&second_token->object.oid, head))
                        return NULL;
        }
        return second_token;
@@ -1038,7 +1038,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
                die_errno(_("could not close '%s'"), filename);
 
        for (pos = 0; pos < merge_names->len; pos = npos) {
-               unsigned char sha1[20];
+               struct object_id oid;
                char *ptr;
                struct commit *commit;
 
@@ -1048,16 +1048,16 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
                else
                        npos = merge_names->len;
 
-               if (npos - pos < 40 + 2 ||
-                   get_sha1_hex(merge_names->buf + pos, sha1))
+               if (npos - pos < GIT_SHA1_HEXSZ + 2 ||
+                   get_oid_hex(merge_names->buf + pos, &oid))
                        commit = NULL; /* bad */
-               else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2))
+               else if (memcmp(merge_names->buf + pos + GIT_SHA1_HEXSZ, "\t\t", 2))
                        continue; /* not-for-merge */
                else {
-                       char saved = merge_names->buf[pos + 40];
-                       merge_names->buf[pos + 40] = '\0';
+                       char saved = merge_names->buf[pos + GIT_SHA1_HEXSZ];
+                       merge_names->buf[pos + GIT_SHA1_HEXSZ] = '\0';
                        commit = get_merge_parent(merge_names->buf + pos);
-                       merge_names->buf[pos + 40] = saved;
+                       merge_names->buf[pos + GIT_SHA1_HEXSZ] = saved;
                }
                if (!commit) {
                        if (ptr)
@@ -1117,9 +1117,7 @@ static struct commit_list *collect_parents(struct commit *head_commit,
 
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
-       unsigned char result_tree[20];
-       unsigned char stash[20];
-       unsigned char head_sha1[20];
+       struct object_id result_tree, stash, head_oid;
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
@@ -1138,13 +1136,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
         */
-       branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, NULL);
+       branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
        if (branch && starts_with(branch, "refs/heads/"))
                branch += 11;
-       if (!branch || is_null_sha1(head_sha1))
+       if (!branch || is_null_oid(&head_oid))
                head_commit = NULL;
        else
-               head_commit = lookup_commit_or_die(head_sha1, "HEAD");
+               head_commit = lookup_commit_or_die(head_oid.hash, "HEAD");
 
        init_diff_ui_defaults();
        git_config(git_merge_config, NULL);
@@ -1242,7 +1240,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * to forbid "git merge" into a branch yet to be born.
                 * We do the same for "git pull".
                 */
-               unsigned char *remote_head_sha1;
+               struct object_id *remote_head_oid;
                if (squash)
                        die(_("Squash commit into empty head not supported yet"));
                if (fast_forward == FF_NO)
@@ -1254,9 +1252,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        die(_("%s - not something we can merge"), argv[0]);
                if (remoteheads->next)
                        die(_("Can merge only exactly one commit into empty head"));
-               remote_head_sha1 = remoteheads->item->object.oid.hash;
-               read_empty(remote_head_sha1, 0);
-               update_ref("initial pull", "HEAD", remote_head_sha1,
+               remote_head_oid = &remoteheads->item->object.oid;
+               read_empty(remote_head_oid->hash, 0);
+               update_ref("initial pull", "HEAD", remote_head_oid->hash,
                           NULL, 0, UPDATE_REFS_DIE_ON_ERR);
                goto done;
        }
@@ -1270,7 +1268,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * additional safety measure to check for it.
         */
        if (!have_message &&
-           is_old_style_invocation(argc, argv, head_commit->object.oid.hash)) {
+           is_old_style_invocation(argc, argv, &head_commit->object.oid)) {
                warning("old-style 'git merge <msg> HEAD <commit>' is deprecated.");
                strbuf_addstr(&merge_msg, argv[0]);
                head_arg = argv[1];
@@ -1422,7 +1420,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               finish(head_commit, remoteheads, commit->object.oid.hash, msg.buf);
+               finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
                drop_save();
                goto done;
        } else if (!remoteheads->next && common->next)
@@ -1441,9 +1439,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        /* See if it is really trivial. */
                        git_committer_info(IDENT_STRICT);
                        printf(_("Trying really trivial in-index merge...\n"));
-                       if (!read_tree_trivial(common->item->object.oid.hash,
-                                              head_commit->object.oid.hash,
-                                              remoteheads->item->object.oid.hash)) {
+                       if (!read_tree_trivial(&common->item->object.oid,
+                                              &head_commit->object.oid,
+                                              &remoteheads->item->object.oid)) {
                                ret = merge_trivial(head_commit, remoteheads);
                                goto done;
                        }
@@ -1495,14 +1493,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
            /*
             * Stash away the local changes so that we can try more than one.
             */
-           save_state(stash))
-               hashclr(stash);
+           save_state(&stash))
+               oidclr(&stash);
 
        for (i = 0; i < use_strategies_nr; i++) {
                int ret;
                if (i) {
                        printf(_("Rewinding the tree to pristine...\n"));
-                       restore_state(head_commit->object.oid.hash, stash);
+                       restore_state(&head_commit->object.oid, &stash);
                }
                if (use_strategies_nr != 1)
                        printf(_("Trying merge strategy %s...\n"),
@@ -1547,7 +1545,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
 
                /* Automerge succeeded. */
-               write_tree_trivial(result_tree);
+               write_tree_trivial(&result_tree);
                automerge_was_ok = 1;
                break;
        }
@@ -1559,7 +1557,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (automerge_was_ok) {
                ret = finish_automerge(head_commit, head_subsumed,
                                       common, remoteheads,
-                                      result_tree, wt_strategy);
+                                      &result_tree, wt_strategy);
                goto done;
        }
 
@@ -1568,7 +1566,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * it up.
         */
        if (!best_strategy) {
-               restore_state(head_commit->object.oid.hash, stash);
+               restore_state(&head_commit->object.oid, &stash);
                if (use_strategies_nr > 1)
                        fprintf(stderr,
                                _("No merge strategy handled the merge.\n"));
@@ -1581,7 +1579,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                ; /* We already have its result in the working tree. */
        else {
                printf(_("Rewinding the tree to pristine...\n"));
-               restore_state(head_commit->object.oid.hash, stash);
+               restore_state(&head_commit->object.oid, &stash);
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
                try_merge_strategy(best_strategy, common, remoteheads,
index 4b492abd417e3520d7ab98725f359087e9fcddd7..0513f7455dccd15e6ec1c2c1e1d847e66af027a7 100644 (file)
@@ -693,7 +693,7 @@ static int merge_abort(struct notes_merge_options *o)
 static int merge_commit(struct notes_merge_options *o)
 {
        struct strbuf msg = STRBUF_INIT;
-       unsigned char sha1[20], parent_sha1[20];
+       struct object_id oid, parent_oid;
        struct notes_tree *t;
        struct commit *partial;
        struct pretty_print_context pretty_ctx;
@@ -705,27 +705,27 @@ static int merge_commit(struct notes_merge_options *o)
         * and target notes ref from .git/NOTES_MERGE_REF.
         */
 
-       if (get_sha1("NOTES_MERGE_PARTIAL", sha1))
+       if (get_oid("NOTES_MERGE_PARTIAL", &oid))
                die(_("failed to read ref NOTES_MERGE_PARTIAL"));
-       else if (!(partial = lookup_commit_reference(sha1)))
+       else if (!(partial = lookup_commit_reference(oid.hash)))
                die(_("could not find commit from NOTES_MERGE_PARTIAL."));
        else if (parse_commit(partial))
                die(_("could not parse commit from NOTES_MERGE_PARTIAL."));
 
        if (partial->parents)
-               hashcpy(parent_sha1, partial->parents->item->object.oid.hash);
+               oidcpy(&parent_oid, &partial->parents->item->object.oid);
        else
-               hashclr(parent_sha1);
+               oidclr(&parent_oid);
 
        t = xcalloc(1, sizeof(struct notes_tree));
        init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 
        o->local_ref = local_ref_to_free =
-               resolve_refdup("NOTES_MERGE_REF", 0, sha1, NULL);
+               resolve_refdup("NOTES_MERGE_REF", 0, oid.hash, NULL);
        if (!o->local_ref)
                die(_("failed to resolve NOTES_MERGE_REF"));
 
-       if (notes_merge_commit(o, t, partial, sha1))
+       if (notes_merge_commit(o, t, partial, oid.hash))
                die(_("failed to finalize notes merge"));
 
        /* Reuse existing commit message in reflog message */
@@ -733,8 +733,8 @@ static int merge_commit(struct notes_merge_options *o)
        format_commit_message(partial, "%s", &msg, &pretty_ctx);
        strbuf_trim(&msg);
        strbuf_insert(&msg, 0, "notes: ", 7);
-       update_ref(msg.buf, o->local_ref, sha1,
-                  is_null_sha1(parent_sha1) ? NULL : parent_sha1,
+       update_ref(msg.buf, o->local_ref, oid.hash,
+                  is_null_oid(&parent_oid) ? NULL : parent_oid.hash,
                   0, UPDATE_REFS_DIE_ON_ERR);
 
        free_notes(t);
index f294dcffa90aa3d8c916e448a4de905fa6363d26..16517f26375b1a0ebe58b628b7741c7f5e77bc08 100644 (file)
@@ -2612,17 +2612,17 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
        free(in_pack.array);
 }
 
-static int add_loose_object(const unsigned char *sha1, const char *path,
+static int add_loose_object(const struct object_id *oid, const char *path,
                            void *data)
 {
-       enum object_type type = sha1_object_info(sha1, NULL);
+       enum object_type type = sha1_object_info(oid->hash, NULL);
 
        if (type < 0) {
                warning("loose object at %s could not be examined", path);
                return 0;
        }
 
-       add_object_entry(sha1, type, "", 0);
+       add_object_entry(oid->hash, type, "", 0);
        return 0;
 }
 
index 7cf900ea0765e334f02df5600f0911d41563a027..c026299e789abe19826264225fa2000b3149f875 100644 (file)
@@ -19,12 +19,12 @@ static int prune_subdir(int nr, const char *path, void *data)
        return 0;
 }
 
-static int prune_object(const unsigned char *sha1, const char *path,
+static int prune_object(const struct object_id *oid, const char *path,
                         void *data)
 {
        int *opts = data;
 
-       if (!has_sha1_pack(sha1))
+       if (!has_sha1_pack(oid->hash))
                return 0;
 
        if (*opts & PRUNE_PACKED_DRY_RUN)
index 8f4f0522856b988a8798fd65c6d81d8826709aa2..42633e0c6e672c46852f0590f1941249691f962c 100644 (file)
@@ -30,7 +30,7 @@ static int prune_tmp_file(const char *fullpath)
        return 0;
 }
 
-static int prune_object(const unsigned char *sha1, const char *fullpath,
+static int prune_object(const struct object_id *oid, const char *fullpath,
                        void *data)
 {
        struct stat st;
@@ -39,7 +39,7 @@ static int prune_object(const unsigned char *sha1, const char *fullpath,
         * Do we know about this object?
         * It must have been reachable
         */
-       if (lookup_object(sha1))
+       if (lookup_object(oid->hash))
                return 0;
 
        if (lstat(fullpath, &st)) {
@@ -50,8 +50,8 @@ static int prune_object(const unsigned char *sha1, const char *fullpath,
        if (st.st_mtime > expire)
                return 0;
        if (show_only || verbose) {
-               enum object_type type = sha1_object_info(sha1, NULL);
-               printf("%s %s\n", sha1_to_hex(sha1),
+               enum object_type type = sha1_object_info(oid->hash, NULL);
+               printf("%s %s\n", oid_to_hex(oid),
                       (type > 0) ? typename(type) : "unknown");
        }
        if (!show_only)
index 9ed8fbbfad71daf45381dbccf0aebe4a5b9af03d..83492af05f2bfd8ca1ec796b7ae8dccc84f0f31b 100644 (file)
@@ -1417,7 +1417,7 @@ static void execute_commands(struct command *commands,
 {
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
        struct command *cmd;
-       unsigned char sha1[20];
+       struct object_id oid;
        struct iterate_data data;
        struct async muxer;
        int err_fd = 0;
@@ -1474,7 +1474,7 @@ static void execute_commands(struct command *commands,
        check_aliased_updates(commands);
 
        free(head_name_to_free);
-       head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
+       head_name = head_name_to_free = resolve_refdup("HEAD", 0, oid.hash, NULL);
 
        if (use_atomic)
                execute_commands_atomic(commands, si);
@@ -1667,8 +1667,11 @@ static const char *unpack(int err_fd, struct shallow_info *si)
        }
 
        tmp_objdir = tmp_objdir_create();
-       if (!tmp_objdir)
+       if (!tmp_objdir) {
+               if (err_fd > 0)
+                       close(err_fd);
                return "unable to create temporary object directory";
+       }
        child.env = tmp_objdir_env(tmp_objdir);
 
        /*
index 7a7136e53e2b8cf1eaa1c79b1183692dd54278a7..747277577857af2c71ab74b83d9ab3aea863c597 100644 (file)
@@ -615,7 +615,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        return status;
 }
 
-static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
index 226d0f95236f299a1506beefb5babf1cf52574f0..f83e7b8fc1758aa837b4dd48708a3d6c31bcd226 100644 (file)
@@ -88,78 +88,78 @@ static int list_replace_refs(const char *pattern, const char *format)
 }
 
 typedef int (*each_replace_name_fn)(const char *name, const char *ref,
-                                   const unsigned char *sha1);
+                                   const struct object_id *oid);
 
 static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
 {
        const char **p, *full_hex;
        char ref[PATH_MAX];
        int had_error = 0;
-       unsigned char sha1[20];
+       struct object_id oid;
 
        for (p = argv; *p; p++) {
-               if (get_sha1(*p, sha1)) {
+               if (get_oid(*p, &oid)) {
                        error("Failed to resolve '%s' as a valid ref.", *p);
                        had_error = 1;
                        continue;
                }
-               full_hex = sha1_to_hex(sha1);
+               full_hex = oid_to_hex(&oid);
                snprintf(ref, sizeof(ref), "%s%s", git_replace_ref_base, full_hex);
                /* read_ref() may reuse the buffer */
                full_hex = ref + strlen(git_replace_ref_base);
-               if (read_ref(ref, sha1)) {
+               if (read_ref(ref, oid.hash)) {
                        error("replace ref '%s' not found.", full_hex);
                        had_error = 1;
                        continue;
                }
-               if (fn(full_hex, ref, sha1))
+               if (fn(full_hex, ref, &oid))
                        had_error = 1;
        }
        return had_error;
 }
 
 static int delete_replace_ref(const char *name, const char *ref,
-                             const unsigned char *sha1)
+                             const struct object_id *oid)
 {
-       if (delete_ref(NULL, ref, sha1, 0))
+       if (delete_ref(NULL, ref, oid->hash, 0))
                return 1;
        printf("Deleted replace ref '%s'\n", name);
        return 0;
 }
 
-static void check_ref_valid(unsigned char object[20],
-                           unsigned char prev[20],
+static void check_ref_valid(struct object_id *object,
+                           struct object_id *prev,
                            char *ref,
                            int ref_size,
                            int force)
 {
        if (snprintf(ref, ref_size,
                     "%s%s", git_replace_ref_base,
-                    sha1_to_hex(object)) > ref_size - 1)
+                    oid_to_hex(object)) > ref_size - 1)
                die("replace ref name too long: %.*s...", 50, ref);
        if (check_refname_format(ref, 0))
                die("'%s' is not a valid ref name.", ref);
 
-       if (read_ref(ref, prev))
-               hashclr(prev);
+       if (read_ref(ref, prev->hash))
+               oidclr(prev);
        else if (!force)
                die("replace ref '%s' already exists", ref);
 }
 
-static int replace_object_sha1(const char *object_ref,
-                              unsigned char object[20],
+static int replace_object_oid(const char *object_ref,
+                              struct object_id *object,
                               const char *replace_ref,
-                              unsigned char repl[20],
+                              struct object_id *repl,
                               int force)
 {
-       unsigned char prev[20];
+       struct object_id prev;
        enum object_type obj_type, repl_type;
        char ref[PATH_MAX];
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
 
-       obj_type = sha1_object_info(object, NULL);
-       repl_type = sha1_object_info(repl, NULL);
+       obj_type = sha1_object_info(object->hash, NULL);
+       repl_type = sha1_object_info(repl->hash, NULL);
        if (!force && obj_type != repl_type)
                die("Objects must be of the same type.\n"
                    "'%s' points to a replaced object of type '%s'\n"
@@ -167,11 +167,11 @@ static int replace_object_sha1(const char *object_ref,
                    object_ref, typename(obj_type),
                    replace_ref, typename(repl_type));
 
-       check_ref_valid(object, prev, ref, sizeof(ref), force);
+       check_ref_valid(object, &prev, ref, sizeof(ref), force);
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
-           ref_transaction_update(transaction, ref, repl, prev,
+           ref_transaction_update(transaction, ref, repl->hash, prev.hash,
                                   0, NULL, &err) ||
            ref_transaction_commit(transaction, &err))
                die("%s", err.buf);
@@ -182,14 +182,14 @@ static int replace_object_sha1(const char *object_ref,
 
 static int replace_object(const char *object_ref, const char *replace_ref, int force)
 {
-       unsigned char object[20], repl[20];
+       struct object_id object, repl;
 
-       if (get_sha1(object_ref, object))
+       if (get_oid(object_ref, &object))
                die("Failed to resolve '%s' as a valid ref.", object_ref);
-       if (get_sha1(replace_ref, repl))
+       if (get_oid(replace_ref, &repl))
                die("Failed to resolve '%s' as a valid ref.", replace_ref);
 
-       return replace_object_sha1(object_ref, object, replace_ref, repl, force);
+       return replace_object_oid(object_ref, &object, replace_ref, &repl, force);
 }
 
 /*
@@ -197,7 +197,7 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
  * If "raw" is true, then the object's raw contents are printed according to
  * "type". Otherwise, we pretty-print the contents for human editing.
  */
-static void export_object(const unsigned char *sha1, enum object_type type,
+static void export_object(const struct object_id *oid, enum object_type type,
                          int raw, const char *filename)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
@@ -213,7 +213,7 @@ static void export_object(const unsigned char *sha1, enum object_type type,
                argv_array_push(&cmd.args, typename(type));
        else
                argv_array_push(&cmd.args, "-p");
-       argv_array_push(&cmd.args, sha1_to_hex(sha1));
+       argv_array_push(&cmd.args, oid_to_hex(oid));
        cmd.git_cmd = 1;
        cmd.out = fd;
 
@@ -226,7 +226,7 @@ static void export_object(const unsigned char *sha1, enum object_type type,
  * interpreting it as "type", and writing the result to the object database.
  * The sha1 of the written object is returned via sha1.
  */
-static void import_object(unsigned char *sha1, enum object_type type,
+static void import_object(struct object_id *oid, enum object_type type,
                          int raw, const char *filename)
 {
        int fd;
@@ -254,7 +254,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
 
                if (finish_command(&cmd))
                        die("mktree reported failure");
-               if (get_sha1_hex(result.buf, sha1) < 0)
+               if (get_oid_hex(result.buf, oid) < 0)
                        die("mktree did not return an object name");
 
                strbuf_release(&result);
@@ -264,7 +264,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
 
                if (fstat(fd, &st) < 0)
                        die_errno("unable to fstat %s", filename);
-               if (index_fd(sha1, fd, &st, type, NULL, flags) < 0)
+               if (index_fd(oid->hash, fd, &st, type, NULL, flags) < 0)
                        die("unable to write object to database");
                /* index_fd close()s fd for us */
        }
@@ -279,29 +279,29 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
 {
        char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
        enum object_type type;
-       unsigned char old[20], new[20], prev[20];
+       struct object_id old, new, prev;
        char ref[PATH_MAX];
 
-       if (get_sha1(object_ref, old) < 0)
+       if (get_oid(object_ref, &old) < 0)
                die("Not a valid object name: '%s'", object_ref);
 
-       type = sha1_object_info(old, NULL);
+       type = sha1_object_info(old.hash, NULL);
        if (type < 0)
-               die("unable to get object type for %s", sha1_to_hex(old));
+               die("unable to get object type for %s", oid_to_hex(&old));
 
-       check_ref_valid(old, prev, ref, sizeof(ref), force);
+       check_ref_valid(&old, &prev, ref, sizeof(ref), force);
 
-       export_object(old, type, raw, tmpfile);
+       export_object(&old, type, raw, tmpfile);
        if (launch_editor(tmpfile, NULL, NULL) < 0)
                die("editing object file failed");
-       import_object(new, type, raw, tmpfile);
+       import_object(&new, type, raw, tmpfile);
 
        free(tmpfile);
 
-       if (!hashcmp(old, new))
-               return error("new object is the same as the old one: '%s'", sha1_to_hex(old));
+       if (!oidcmp(&old, &new))
+               return error("new object is the same as the old one: '%s'", oid_to_hex(&old));
 
-       return replace_object_sha1(object_ref, old, "replacement", new, force);
+       return replace_object_oid(object_ref, &old, "replacement", &new, force);
 }
 
 static void replace_parents(struct strbuf *buf, int argc, const char **argv)
@@ -312,7 +312,7 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv)
 
        /* find existing parents */
        parent_start = buf->buf;
-       parent_start += 46; /* "tree " + "hex sha1" + "\n" */
+       parent_start += GIT_SHA1_HEXSZ + 6; /* "tree " + "hex sha1" + "\n" */
        parent_end = parent_start;
 
        while (starts_with(parent_end, "parent "))
@@ -320,11 +320,11 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv)
 
        /* prepare new parents */
        for (i = 0; i < argc; i++) {
-               unsigned char sha1[20];
-               if (get_sha1(argv[i], sha1) < 0)
+               struct object_id oid;
+               if (get_oid(argv[i], &oid) < 0)
                        die(_("Not a valid object name: '%s'"), argv[i]);
-               lookup_commit_or_die(sha1, argv[i]);
-               strbuf_addf(&new_parents, "parent %s\n", sha1_to_hex(sha1));
+               lookup_commit_or_die(oid.hash, argv[i]);
+               strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid));
        }
 
        /* replace existing parents with new ones */
@@ -345,12 +345,12 @@ static void check_one_mergetag(struct commit *commit,
 {
        struct check_mergetag_data *mergetag_data = (struct check_mergetag_data *)data;
        const char *ref = mergetag_data->argv[0];
-       unsigned char tag_sha1[20];
+       struct object_id tag_oid;
        struct tag *tag;
        int i;
 
-       hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_sha1);
-       tag = lookup_tag(tag_sha1);
+       hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_oid.hash);
+       tag = lookup_tag(tag_oid.hash);
        if (!tag)
                die(_("bad mergetag in commit '%s'"), ref);
        if (parse_tag_buffer(tag, extra->value, extra->len))
@@ -366,7 +366,7 @@ static void check_one_mergetag(struct commit *commit,
        }
 
        die(_("original commit '%s' contains mergetag '%s' that is discarded; "
-             "use --edit instead of --graft"), ref, sha1_to_hex(tag_sha1));
+             "use --edit instead of --graft"), ref, oid_to_hex(&tag_oid));
 }
 
 static void check_mergetags(struct commit *commit, int argc, const char **argv)
@@ -380,16 +380,16 @@ static void check_mergetags(struct commit *commit, int argc, const char **argv)
 
 static int create_graft(int argc, const char **argv, int force)
 {
-       unsigned char old[20], new[20];
+       struct object_id old, new;
        const char *old_ref = argv[0];
        struct commit *commit;
        struct strbuf buf = STRBUF_INIT;
        const char *buffer;
        unsigned long size;
 
-       if (get_sha1(old_ref, old) < 0)
+       if (get_oid(old_ref, &old) < 0)
                die(_("Not a valid object name: '%s'"), old_ref);
-       commit = lookup_commit_or_die(old, old_ref);
+       commit = lookup_commit_or_die(old.hash, old_ref);
 
        buffer = get_commit_buffer(commit, &size);
        strbuf_add(&buf, buffer, size);
@@ -404,15 +404,15 @@ static int create_graft(int argc, const char **argv, int force)
 
        check_mergetags(commit, argc, argv);
 
-       if (write_sha1_file(buf.buf, buf.len, commit_type, new))
+       if (write_sha1_file(buf.buf, buf.len, commit_type, new.hash))
                die(_("could not write replacement commit for: '%s'"), old_ref);
 
        strbuf_release(&buf);
 
-       if (!hashcmp(old, new))
-               return error("new commit is the same as the old one: '%s'", sha1_to_hex(old));
+       if (!oidcmp(&old, &new))
+               return error("new commit is the same as the old one: '%s'", oid_to_hex(&old));
 
-       return replace_object_sha1(old_ref, old, "replacement", new, force);
+       return replace_object_oid(old_ref, &old, "replacement", &new, force);
 }
 
 int cmd_replace(int argc, const char **argv, const char *prefix)
index e08677e559f443f8a290f200f691ef770ecde3ef..1e5bdea0d5ba598b4191238fee761295bd8523ab 100644 (file)
@@ -12,6 +12,7 @@
 #include "diff.h"
 #include "revision.h"
 #include "split-index.h"
+#include "submodule.h"
 
 #define DO_REVS                1
 #define DO_NOREV       2
@@ -535,6 +536,34 @@ N_("git rev-parse --parseopt [<options>] -- [<args>...]\n"
    "\n"
    "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
 
+/*
+ * Parse "opt" or "opt=<value>", setting value respectively to either
+ * NULL or the string after "=".
+ */
+static int opt_with_value(const char *arg, const char *opt, const char **value)
+{
+       if (skip_prefix(arg, opt, &arg)) {
+               if (!*arg) {
+                       *value = NULL;
+                       return 1;
+               }
+               if (*arg++ == '=') {
+                       *value = arg;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static void handle_ref_opt(const char *pattern, const char *prefix)
+{
+       if (pattern)
+               for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
+       else
+               for_each_ref_in(prefix, show_reference, NULL);
+       clear_ref_exclusion(&ref_excludes);
+}
+
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
@@ -674,14 +703,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                flags |= GET_SHA1_QUIETLY;
                                continue;
                        }
-                       if (!strcmp(arg, "--short") ||
-                           starts_with(arg, "--short=")) {
+                       if (opt_with_value(arg, "--short", &arg)) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
                                abbrev = DEFAULT_ABBREV;
-                               if (!arg[7])
+                               if (!arg)
                                        continue;
-                               abbrev = strtoul(arg + 8, NULL, 10);
+                               abbrev = strtoul(arg, NULL, 10);
                                if (abbrev < MINIMUM_ABBREV)
                                        abbrev = MINIMUM_ABBREV;
                                else if (40 <= abbrev)
@@ -704,17 +732,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                symbolic = SHOW_SYMBOLIC_FULL;
                                continue;
                        }
-                       if (starts_with(arg, "--abbrev-ref") &&
-                           (!arg[12] || arg[12] == '=')) {
+                       if (opt_with_value(arg, "--abbrev-ref", &arg)) {
                                abbrev_ref = 1;
                                abbrev_ref_strict = warn_ambiguous_refs;
-                               if (arg[12] == '=') {
-                                       if (!strcmp(arg + 13, "strict"))
+                               if (arg) {
+                                       if (!strcmp(arg, "strict"))
                                                abbrev_ref_strict = 1;
-                                       else if (!strcmp(arg + 13, "loose"))
+                                       else if (!strcmp(arg, "loose"))
                                                abbrev_ref_strict = 0;
                                        else
-                                               die("unknown mode for %s", arg);
+                                               die("unknown mode for --abbrev-ref: %s",
+                                                   arg);
                                }
                                continue;
                        }
@@ -722,8 +750,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
-                       if (starts_with(arg, "--disambiguate=")) {
-                               for_each_abbrev(arg + 15, show_abbrev, NULL);
+                       if (skip_prefix(arg, "--disambiguate=", &arg)) {
+                               for_each_abbrev(arg, show_abbrev, NULL);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
@@ -731,46 +759,24 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
                                continue;
                        }
-                       if (starts_with(arg, "--branches=")) {
-                               for_each_glob_ref_in(show_reference, arg + 11,
-                                       "refs/heads/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--branches", &arg)) {
+                               handle_ref_opt(arg, "refs/heads/");
                                continue;
                        }
-                       if (!strcmp(arg, "--branches")) {
-                               for_each_branch_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--tags", &arg)) {
+                               handle_ref_opt(arg, "refs/tags/");
                                continue;
                        }
-                       if (starts_with(arg, "--tags=")) {
-                               for_each_glob_ref_in(show_reference, arg + 7,
-                                       "refs/tags/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (skip_prefix(arg, "--glob=", &arg)) {
+                               handle_ref_opt(arg, NULL);
                                continue;
                        }
-                       if (!strcmp(arg, "--tags")) {
-                               for_each_tag_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--remotes", &arg)) {
+                               handle_ref_opt(arg, "refs/remotes/");
                                continue;
                        }
-                       if (starts_with(arg, "--glob=")) {
-                               for_each_glob_ref(show_reference, arg + 7, NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (starts_with(arg, "--remotes=")) {
-                               for_each_glob_ref_in(show_reference, arg + 10,
-                                       "refs/remotes/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (!strcmp(arg, "--remotes")) {
-                               for_each_remote_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (starts_with(arg, "--exclude=")) {
-                               add_ref_exclusion(&ref_excludes, arg + 10);
+                       if (skip_prefix(arg, "--exclude=", &arg)) {
+                               add_ref_exclusion(&ref_excludes, arg);
                                continue;
                        }
                        if (!strcmp(arg, "--show-toplevel")) {
@@ -779,6 +785,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                        puts(work_tree);
                                continue;
                        }
+                       if (!strcmp(arg, "--show-superproject-working-tree")) {
+                               const char *superproject = get_superproject_working_tree();
+                               if (superproject)
+                                       puts(superproject);
+                               continue;
+                       }
                        if (!strcmp(arg, "--show-prefix")) {
                                if (prefix)
                                        puts(prefix);
@@ -865,20 +877,20 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                }
                                continue;
                        }
-                       if (starts_with(arg, "--since=")) {
-                               show_datestring("--max-age=", arg+8);
+                       if (skip_prefix(arg, "--since=", &arg)) {
+                               show_datestring("--max-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--after=")) {
-                               show_datestring("--max-age=", arg+8);
+                       if (skip_prefix(arg, "--after=", &arg)) {
+                               show_datestring("--max-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--before=")) {
-                               show_datestring("--min-age=", arg+9);
+                       if (skip_prefix(arg, "--before=", &arg)) {
+                               show_datestring("--min-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--until=")) {
-                               show_datestring("--min-age=", arg+8);
+                       if (skip_prefix(arg, "--until=", &arg)) {
+                               show_datestring("--min-age=", arg);
                                continue;
                        }
                        if (show_flag(arg) && verify)
index 4ca5b515449a355ae04f0ff496a8ee0b7143e29a..345d9586a709c08a1095f2635833d075c45fe7be 100644 (file)
@@ -54,6 +54,24 @@ static int option_parse_x(const struct option *opt,
        return 0;
 }
 
+static int option_parse_m(const struct option *opt,
+                         const char *arg, int unset)
+{
+       struct replay_opts *replay = opt->value;
+       char *end;
+
+       if (unset) {
+               replay->mainline = 0;
+               return 0;
+       }
+
+       replay->mainline = strtol(arg, &end, 10);
+       if (*end || replay->mainline <= 0)
+               return opterror(opt, "expects a number greater than zero", 0);
+
+       return 0;
+}
+
 LAST_ARG_MUST_BE_NULL
 static void verify_opt_compatible(const char *me, const char *base_opt, ...)
 {
@@ -84,7 +102,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
                OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
                OPT_NOOP_NOARG('r', NULL),
                OPT_BOOL('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
-               OPT_INTEGER('m', "mainline", &opts->mainline, N_("parent number")),
+               OPT_CALLBACK('m', "mainline", opts, N_("parent-number"),
+                            N_("select mainline parent"), option_parse_m),
                OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
                OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
                OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
index d530e89368b42bf1465784b80163b9158a06585a..d74d72cc7fad93980abd409d2c9dc10ae4652fd3 100644 (file)
@@ -1099,17 +1099,20 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        }
 
        if (split_index > 0) {
-               init_split_index(&the_index);
-               the_index.cache_changed |= SPLIT_INDEX_ORDERED;
-       } else if (!split_index && the_index.split_index) {
-               /*
-                * can't discard_split_index(&the_index); because that
-                * will destroy split_index->base->cache[], which may
-                * be shared with the_index.cache[]. So yeah we're
-                * leaking a bit here.
-                */
-               the_index.split_index = NULL;
-               the_index.cache_changed |= SOMETHING_CHANGED;
+               if (git_config_get_split_index() == 0)
+                       warning(_("core.splitIndex is set to false; "
+                                 "remove or change it, if you really want to "
+                                 "enable split index"));
+               if (the_index.split_index)
+                       the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+               else
+                       add_split_index(&the_index);
+       } else if (!split_index) {
+               if (git_config_get_split_index() == 1)
+                       warning(_("core.splitIndex is set to true; "
+                                 "remove or change it, if you really want to "
+                                 "disable split index"));
+               remove_split_index(&the_index);
        }
 
        switch (untracked_cache) {
diff --git a/cache.h b/cache.h
index 5db29a945381a1b2347fa675ca6e94d818855e2d..2214d52f6127f20ad9788402b0e448977db615c5 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -10,8 +10,8 @@
 #include "trace.h"
 #include "string-list.h"
 #include "pack-revindex.h"
+#include "hash.h"
 
-#include SHA1_HEADER
 #ifndef platform_SHA_CTX
 /*
  * platform's underlying implementation of SHA-1; could be OpenSSL,
@@ -518,6 +518,13 @@ extern void set_git_work_tree(const char *tree);
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
 extern void setup_work_tree(void);
+/*
+ * Find GIT_DIR of the repository that contains the current working directory,
+ * without changing the working directory or other global state. The result is
+ * appended to gitdir. The return value is either NULL if no repository was
+ * found, or pointing to the path inside gitdir's buffer.
+ */
+extern const char *discover_git_directory(struct strbuf *gitdir);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
 extern char *prefix_path(const char *prefix, int len, const char *path);
@@ -1277,6 +1284,9 @@ extern int has_pack_index(const unsigned char *sha1);
 
 extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
 
+/* Helper to check and "touch" a file */
+extern int check_and_freshen_file(const char *fn, int freshen);
+
 extern const signed char hexval_table[256];
 static inline unsigned int hexval(unsigned char c)
 {
@@ -1367,7 +1377,46 @@ extern char *oid_to_hex_r(char *out, const struct object_id *oid);
 extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
 extern char *oid_to_hex(const struct object_id *oid);  /* same static buffer as sha1_to_hex */
 
-extern int interpret_branch_name(const char *str, int len, struct strbuf *);
+/*
+ * Parse a 40-character hexadecimal object ID starting from hex, updating the
+ * pointer specified by end when parsing stops.  The resulting object ID is
+ * stored in oid.  Returns 0 on success.  Parsing will stop on the first NUL or
+ * other invalid character.  end is only updated on success; otherwise, it is
+ * unmodified.
+ */
+extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+
+/*
+ * This reads short-hand syntax that not only evaluates to a commit
+ * object name, but also can act as if the end user spelled the name
+ * of the branch from the command line.
+ *
+ * - "@{-N}" finds the name of the Nth previous branch we were on, and
+ *   places the name of the branch in the given buf and returns the
+ *   number of characters parsed if successful.
+ *
+ * - "<branch>@{upstream}" finds the name of the other ref that
+ *   <branch> is configured to merge with (missing <branch> defaults
+ *   to the current branch), and places the name of the branch in the
+ *   given buf and returns the number of characters parsed if
+ *   successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ *
+ * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+ * expansions: local branches ("refs/heads/"), remote branches
+ * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+ * allowed, even ones to refs outside of those namespaces.
+ */
+#define INTERPRET_BRANCH_LOCAL (1<<0)
+#define INTERPRET_BRANCH_REMOTE (1<<1)
+#define INTERPRET_BRANCH_HEAD (1<<2)
+extern int interpret_branch_name(const char *str, int len, struct strbuf *,
+                                unsigned allowed);
 extern int get_oid_mb(const char *str, struct object_id *oid);
 
 extern int validate_headref(const char *ref);
@@ -1611,6 +1660,27 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 
 extern void pack_report(void);
 
+/*
+ * Create a temporary file rooted in the object database directory.
+ */
+extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
+
+/*
+ * Generate the filename to be used for a pack file with checksum "sha1" and
+ * extension "ext". The result is written into the strbuf "buf", overwriting
+ * any existing contents. A pointer to buf->buf is returned as a convenience.
+ *
+ * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
+ */
+extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
+
+/*
+ * Create a pack .keep file named "name" (which should generally be the output
+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
+ * error.
+ */
+extern int odb_pack_keep(const char *name);
+
 /*
  * mmap the index file for the specified packfile (if it is not
  * already mmapped).  Return 0 on success.
@@ -1647,6 +1717,12 @@ extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
  * error.
  */
 extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
+/*
+ * Like nth_packed_object_sha1, but write the data into the object specified by
+ * the the first argument.  Returns the first argument on success, and NULL on
+ * error.
+ */
+extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
 
 /*
  * Return the offset of the nth object within the specified packfile.
@@ -1688,7 +1764,7 @@ extern int unpack_object_header(struct packed_git *, struct pack_window **, off_
  * scratch buffer, but restored to its original contents before
  * the function returns.
  */
-typedef int each_loose_object_fn(const unsigned char *sha1,
+typedef int each_loose_object_fn(const struct object_id *oid,
                                 const char *path,
                                 void *data);
 typedef int each_loose_cruft_fn(const char *basename,
@@ -1714,7 +1790,7 @@ int for_each_loose_file_in_objdir_buf(struct strbuf *path,
  * LOCAL_ONLY flag is set).
  */
 #define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
-typedef int each_packed_object_fn(const unsigned char *sha1,
+typedef int each_packed_object_fn(const struct object_id *oid,
                                  struct packed_git *pack,
                                  uint32_t pos,
                                  void *data);
@@ -1801,6 +1877,7 @@ extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
                                     const unsigned char *sha1, void *data);
 extern void git_config_push_parameter(const char *text);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
+extern void read_early_config(config_fn_t cb, void *data);
 extern void git_config(config_fn_t fn, void *);
 extern int git_config_with_options(config_fn_t fn, void *,
                                   struct git_config_source *config_source,
@@ -1933,6 +2010,11 @@ extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
 extern int git_config_get_maybe_bool(const char *key, int *dest);
 extern int git_config_get_pathname(const char *key, const char **dest);
 extern int git_config_get_untracked_cache(void);
+extern int git_config_get_split_index(void);
+extern int git_config_get_max_percent_split_change(void);
+
+/* This dies if the configured or default date is in the future */
+extern int git_config_get_expiry(const char *key, const char **output);
 
 /*
  * This is a hack for test programs like test-dump-untracked-cache to
diff --git a/ci/run-linux32-build.sh b/ci/run-linux32-build.sh
new file mode 100755 (executable)
index 0000000..e30fb2c
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# Build and test Git in a 32-bit environment
+#
+# Usage:
+#   run-linux32-build.sh [host-user-id]
+#
+
+# Update packages to the latest available versions
+linux32 --32bit i386 sh -c '
+    apt update >/dev/null &&
+    apt install -y build-essential libcurl4-openssl-dev libssl-dev \
+       libexpat-dev gettext python >/dev/null
+' &&
+
+# If this script runs inside a docker container, then all commands are
+# usually executed as root. Consequently, the host user might not be
+# able to access the test output files.
+# If a host user id is given, then create a user "ci" with the host user
+# id to make everything accessible to the host user.
+HOST_UID=$1 &&
+CI_USER=$USER &&
+test -z $HOST_UID || (CI_USER="ci" && useradd -u $HOST_UID $CI_USER) &&
+
+# Build and test
+linux32 --32bit i386 su -m -l $CI_USER -c '
+    cd /usr/src/git &&
+    make --jobs=2 &&
+    make --quiet test
+'
index 0e9e1ebefc0a6771de7bf20615e41176ed986179..1a4d85537b3c96749999250a34216257db134714 100644 (file)
--- a/config.c
+++ b/config.c
@@ -13,6 +13,7 @@
 #include "hashmap.h"
 #include "string-list.h"
 #include "utf8.h"
+#include "dir.h"
 
 struct config_source {
        struct config_source *prev;
@@ -170,9 +171,94 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        return ret;
 }
 
+static int prepare_include_condition_pattern(struct strbuf *pat)
+{
+       struct strbuf path = STRBUF_INIT;
+       char *expanded;
+       int prefix = 0;
+
+       expanded = expand_user_path(pat->buf);
+       if (expanded) {
+               strbuf_reset(pat);
+               strbuf_addstr(pat, expanded);
+               free(expanded);
+       }
+
+       if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
+               const char *slash;
+
+               if (!cf || !cf->path)
+                       return error(_("relative config include "
+                                      "conditionals must come from files"));
+
+               strbuf_add_absolute_path(&path, cf->path);
+               slash = find_last_dir_sep(path.buf);
+               if (!slash)
+                       die("BUG: how is this possible?");
+               strbuf_splice(pat, 0, 1, path.buf, slash - path.buf);
+               prefix = slash - path.buf + 1 /* slash */;
+       } else if (!is_absolute_path(pat->buf))
+               strbuf_insert(pat, 0, "**/", 3);
+
+       if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
+               strbuf_addstr(pat, "**");
+
+       strbuf_release(&path);
+       return prefix;
+}
+
+static int include_by_gitdir(const char *cond, size_t cond_len, int icase)
+{
+       struct strbuf text = STRBUF_INIT;
+       struct strbuf pattern = STRBUF_INIT;
+       int ret = 0, prefix;
+
+       strbuf_add_absolute_path(&text, get_git_dir());
+       strbuf_add(&pattern, cond, cond_len);
+       prefix = prepare_include_condition_pattern(&pattern);
+
+       if (prefix < 0)
+               goto done;
+
+       if (prefix > 0) {
+               /*
+                * perform literal matching on the prefix part so that
+                * any wildcard character in it can't create side effects.
+                */
+               if (text.len < prefix)
+                       goto done;
+               if (!icase && strncmp(pattern.buf, text.buf, prefix))
+                       goto done;
+               if (icase && strncasecmp(pattern.buf, text.buf, prefix))
+                       goto done;
+       }
+
+       ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
+                        icase ? WM_CASEFOLD : 0, NULL);
+
+done:
+       strbuf_release(&pattern);
+       strbuf_release(&text);
+       return ret;
+}
+
+static int include_condition_is_true(const char *cond, size_t cond_len)
+{
+
+       if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
+               return include_by_gitdir(cond, cond_len, 0);
+       else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
+               return include_by_gitdir(cond, cond_len, 1);
+
+       /* unknown conditionals are always false */
+       return 0;
+}
+
 int git_config_include(const char *var, const char *value, void *data)
 {
        struct config_include_data *inc = data;
+       const char *cond, *key;
+       int cond_len;
        int ret;
 
        /*
@@ -185,6 +271,12 @@ int git_config_include(const char *var, const char *value, void *data)
 
        if (!strcmp(var, "include.path"))
                ret = handle_path_include(value, inc);
+
+       if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
+           (cond && include_condition_is_true(cond, cond_len)) &&
+           !strcmp(key, "path"))
+               ret = handle_path_include(value, inc);
+
        return ret;
 }
 
@@ -1503,6 +1595,31 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
        }
 }
 
+void read_early_config(config_fn_t cb, void *data)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       git_config_with_options(cb, data, NULL, 1);
+
+       /*
+        * When setup_git_directory() was not yet asked to discover the
+        * GIT_DIR, we ask discover_git_directory() to figure out whether there
+        * is any repository config we should use (but unlike
+        * setup_git_directory_gently(), no global state is changed, most
+        * notably, the current working directory is still the same after the
+        * call).
+        */
+       if (!have_git_dir() && discover_git_directory(&buf)) {
+               struct git_config_source repo_config;
+
+               memset(&repo_config, 0, sizeof(repo_config));
+               strbuf_addstr(&buf, "/config");
+               repo_config.file = buf.buf;
+               git_config_with_options(cb, data, &repo_config, 1);
+       }
+       strbuf_release(&buf);
+}
+
 static void git_config_check_init(void);
 
 void git_config(config_fn_t fn, void *data)
@@ -1803,6 +1920,19 @@ int git_config_get_pathname(const char *key, const char **dest)
        return ret;
 }
 
+int git_config_get_expiry(const char *key, const char **output)
+{
+       int ret = git_config_get_string_const(key, output);
+       if (ret)
+               return ret;
+       if (strcmp(*output, "now")) {
+               unsigned long now = approxidate("now");
+               if (approxidate(*output) >= now)
+                       git_die_config(key, _("Invalid %s: '%s'"), key, *output);
+       }
+       return ret;
+}
+
 int git_config_get_untracked_cache(void)
 {
        int val = -1;
@@ -1819,14 +1949,39 @@ int git_config_get_untracked_cache(void)
                if (!strcasecmp(v, "keep"))
                        return -1;
 
-               error("unknown core.untrackedCache value '%s'; "
-                     "using 'keep' default value", v);
+               error(_("unknown core.untrackedCache value '%s'; "
+                       "using 'keep' default value"), v);
                return -1;
        }
 
        return -1; /* default value */
 }
 
+int git_config_get_split_index(void)
+{
+       int val;
+
+       if (!git_config_get_maybe_bool("core.splitindex", &val))
+               return val;
+
+       return -1; /* default value */
+}
+
+int git_config_get_max_percent_split_change(void)
+{
+       int val = -1;
+
+       if (!git_config_get_int("splitindex.maxpercentchange", &val)) {
+               if (0 <= val && val <= 100)
+                       return val;
+
+               return error(_("splitIndex.maxPercentChange value '%d' "
+                              "should be between 0 and 100"), val);
+       }
+
+       return -1; /* default value */
+}
+
 NORETURN
 void git_die_config_linenr(const char *key, const char *filename, int linenr)
 {
index 97eacd7832b28377c5e14918ce6bededc55d7d40..c6cbef38c2a580555dfcdc67e04f6ac507f9066c 100644 (file)
@@ -82,6 +82,7 @@
 #     contains      relative to newer annotated tag (v1.6.3.2~35)
 #     branch        relative to newer tag or branch (master~4)
 #     describe      relative to older annotated tag (v1.6.3.1-13-gdd42c2f)
+#     tag           relative to any older tag (v1.6.3.1-13-gdd42c2f)
 #     default       exactly matching tag
 #
 # If you would like a colored hint about the current dirty state, set
@@ -443,6 +444,8 @@ __git_ps1 ()
                                        git describe --contains HEAD ;;
                                (branch)
                                        git describe --contains --all HEAD ;;
+                               (tag)
+                                       git describe --tags HEAD ;;
                                (describe)
                                        git describe HEAD ;;
                                (* | default)
diff --git a/dir.c b/dir.c
index aeeb5ce10490ef1e0adb907d1e21bcbbbbb401ac..837ff965a470e74028fce6019f4584e55dcf445c 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -9,6 +9,7 @@
  */
 #include "cache.h"
 #include "dir.h"
+#include "attr.h"
 #include "refs.h"
 #include "wildmatch.h"
 #include "pathspec.h"
@@ -134,7 +135,8 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        for (n = 0; n < pathspec->nr; n++) {
                size_t i = 0, len = 0, item_len;
@@ -209,6 +211,36 @@ int within_depth(const char *name, int namelen,
 #define DO_MATCH_DIRECTORY (1<<1)
 #define DO_MATCH_SUBMODULE (1<<2)
 
+static int match_attrs(const char *name, int namelen,
+                      const struct pathspec_item *item)
+{
+       int i;
+
+       git_check_attr(name, item->attr_check);
+       for (i = 0; i < item->attr_match_nr; i++) {
+               const char *value;
+               int matched;
+               enum attr_match_mode match_mode;
+
+               value = item->attr_check->items[i].value;
+               match_mode = item->attr_match[i].match_mode;
+
+               if (ATTR_TRUE(value))
+                       matched = (match_mode == MATCH_SET);
+               else if (ATTR_FALSE(value))
+                       matched = (match_mode == MATCH_UNSET);
+               else if (ATTR_UNSET(value))
+                       matched = (match_mode == MATCH_UNSPECIFIED);
+               else
+                       matched = (match_mode == MATCH_VALUE &&
+                                  !strcmp(item->attr_match[i].value, value));
+               if (!matched)
+                       return 0;
+       }
+
+       return 1;
+}
+
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -261,6 +293,9 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
            strncmp(item->match, name - prefix, item->prefix))
                return 0;
 
+       if (item->attr_match_nr && !match_attrs(name, namelen, item))
+               return 0;
+
        /* If the match was just the prefix, we matched */
        if (!*match)
                return MATCHED_RECURSIVELY;
@@ -339,7 +374,8 @@ static int do_match_pathspec(const struct pathspec *ps,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -1361,7 +1397,8 @@ static int simplify_away(const char *path, int pathlen,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        for (i = 0; i < pathspec->nr; i++) {
                const struct pathspec_item *item = &pathspec->items[i];
index 42dc3106d2fae1976dcfdfbf264a1a9ed96a71b6..2fdba76222b8f5744f1ae84fd11e72a7a3c115cf 100644 (file)
@@ -296,18 +296,16 @@ int odb_mkstemp(char *template, size_t limit, const char *pattern)
        return xmkstemp_mode(template, mode);
 }
 
-int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1)
+int odb_pack_keep(const char *name)
 {
        int fd;
 
-       snprintf(name, namesz, "%s/pack/pack-%s.keep",
-                get_object_directory(), sha1_to_hex(sha1));
        fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
        if (0 <= fd)
                return fd;
 
        /* slow path */
-       safe_create_leading_directories(name);
+       safe_create_leading_directories_const(name);
        return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
 }
 
index 6c13472c42257a47b07058156a7070cf427a038d..41a539f97d94fdd45787c520ceec4b5c2b6102d4 100644 (file)
@@ -940,41 +940,40 @@ static const char *create_index(void)
 
 static char *keep_pack(const char *curr_index_name)
 {
-       static char name[PATH_MAX];
        static const char *keep_msg = "fast-import";
+       struct strbuf name = STRBUF_INIT;
        int keep_fd;
 
-       keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1);
+       odb_pack_name(&name, pack_data->sha1, "keep");
+       keep_fd = odb_pack_keep(name.buf);
        if (keep_fd < 0)
                die_errno("cannot create keep file");
        write_or_die(keep_fd, keep_msg, strlen(keep_msg));
        if (close(keep_fd))
                die_errno("failed to write keep file");
 
-       snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
-                get_object_directory(), sha1_to_hex(pack_data->sha1));
-       if (finalize_object_file(pack_data->pack_name, name))
+       odb_pack_name(&name, pack_data->sha1, "pack");
+       if (finalize_object_file(pack_data->pack_name, name.buf))
                die("cannot store pack file");
 
-       snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
-                get_object_directory(), sha1_to_hex(pack_data->sha1));
-       if (finalize_object_file(curr_index_name, name))
+       odb_pack_name(&name, pack_data->sha1, "idx");
+       if (finalize_object_file(curr_index_name, name.buf))
                die("cannot store index file");
        free((void *)curr_index_name);
-       return name;
+       return strbuf_detach(&name, NULL);
 }
 
 static void unkeep_all_packs(void)
 {
-       static char name[PATH_MAX];
+       struct strbuf name = STRBUF_INIT;
        int k;
 
        for (k = 0; k < pack_id; k++) {
                struct packed_git *p = all_packs[k];
-               snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
-                        get_object_directory(), sha1_to_hex(p->sha1));
-               unlink_or_warn(name);
+               odb_pack_name(&name, p->sha1, "keep");
+               unlink_or_warn(name.buf);
        }
+       strbuf_release(&name);
 }
 
 static int loosen_small_pack(const struct packed_git *p)
@@ -1033,6 +1032,7 @@ static void end_packfile(void)
                        die("core git rejected index %s", idx_name);
                all_packs[pack_id] = new_p;
                install_packed_git(new_p);
+               free(idx_name);
 
                /* Print the boundary */
                if (pack_edges) {
index e0f5d5ce875acad79b5ddcfc4e24b6ff8bc33df9..d07d85ce302d39abc8c017c377d4230d729fdeb4 100644 (file)
@@ -614,7 +614,7 @@ static void filter_refs(struct fetch_pack_args *args,
                                        break; /* definitely do not have it */
                                else if (cmp == 0) {
                                        keep = 1; /* definitely have it */
-                                       sought[i]->matched = 1;
+                                       sought[i]->match_status = REF_MATCHED;
                                }
                                i++;
                        }
@@ -634,22 +634,24 @@ static void filter_refs(struct fetch_pack_args *args,
        }
 
        /* Append unmatched requests to the list */
-       if ((allow_unadvertised_object_request &
-           (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
-               for (i = 0; i < nr_sought; i++) {
-                       unsigned char sha1[20];
+       for (i = 0; i < nr_sought; i++) {
+               unsigned char sha1[20];
 
-                       ref = sought[i];
-                       if (ref->matched)
-                               continue;
-                       if (get_sha1_hex(ref->name, sha1) ||
-                           ref->name[40] != '\0' ||
-                           hashcmp(sha1, ref->old_oid.hash))
-                               continue;
+               ref = sought[i];
+               if (ref->match_status != REF_NOT_MATCHED)
+                       continue;
+               if (get_sha1_hex(ref->name, sha1) ||
+                   ref->name[40] != '\0' ||
+                   hashcmp(sha1, ref->old_oid.hash))
+                       continue;
 
-                       ref->matched = 1;
+               if ((allow_unadvertised_object_request &
+                   (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
+                       ref->match_status = REF_MATCHED;
                        *newtail = copy_ref(ref);
                        newtail = &(*newtail)->next;
+               } else {
+                       ref->match_status = REF_UNADVERTISED_NOT_ALLOWED;
                }
        }
        *refs = newlist;
@@ -1130,3 +1132,26 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
        clear_shallow_info(&si);
        return ref_cpy;
 }
+
+int report_unmatched_refs(struct ref **sought, int nr_sought)
+{
+       int i, ret = 0;
+
+       for (i = 0; i < nr_sought; i++) {
+               if (!sought[i])
+                       continue;
+               switch (sought[i]->match_status) {
+               case REF_MATCHED:
+                       continue;
+               case REF_NOT_MATCHED:
+                       error(_("no such remote ref %s"), sought[i]->name);
+                       break;
+               case REF_UNADVERTISED_NOT_ALLOWED:
+                       error(_("Server does not allow request for unadvertised object %s"),
+                             sought[i]->name);
+                       break;
+               }
+               ret = 1;
+       }
+       return ret;
+}
index c912e3d321c6a80952dbcd266786d1042a49e579..a2d46e6e754fef713c42fa34b29588eb14bd0e6a 100644 (file)
@@ -45,4 +45,10 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                       struct sha1_array *shallow,
                       char **pack_lockfile);
 
+/*
+ * Print an appropriate error message for each sought ref that wasn't
+ * matched.  Return 0 if all sought refs were matched, otherwise 1.
+ */
+int report_unmatched_refs(struct ref **sought, int nr_sought);
+
 #endif
index f5c816e273318709e90a9f5e9a47143a3842f8e3..77b4ed53a8aa76aa03bac8324a297e5c9ca5c340 100755 (executable)
@@ -275,20 +275,11 @@ sub list_modified {
        my ($only) = @_;
        my (%data, @return);
        my ($add, $del, $adddel, $file);
-       my @tracked = ();
-
-       if (@ARGV) {
-               @tracked = map {
-                       chomp $_;
-                       unquote_path($_);
-               } run_cmd_pipe(qw(git ls-files --), @ARGV);
-               return if (!@tracked);
-       }
 
        my $reference = get_diff_reference($patch_mode_revision);
        for (run_cmd_pipe(qw(git diff-index --cached
                             --numstat --summary), $reference,
-                            '--', @tracked)) {
+                            '--', @ARGV)) {
                if (($add, $del, $file) =
                    /^([-\d]+)  ([-\d]+)        (.*)/) {
                        my ($change, $bin);
@@ -313,7 +304,7 @@ sub list_modified {
                }
        }
 
-       for (run_cmd_pipe(qw(git diff-files --numstat --summary --raw --), @tracked)) {
+       for (run_cmd_pipe(qw(git diff-files --numstat --summary --raw --), @ARGV)) {
                if (($add, $del, $file) =
                    /^([-\d]+)  ([-\d]+)        (.*)/) {
                        $file = unquote_path($file);
index e626851fe98673b4ac546864030975870ab4dfa7..8a4a3f85e7ba0a658420c477e1af82e804d7fd36 100644 (file)
@@ -798,8 +798,6 @@ extern FILE *xfopen(const char *path, const char *mode);
 extern FILE *xfdopen(int fd, const char *mode);
 extern int xmkstemp(char *template);
 extern int xmkstemp_mode(char *template, int mode);
-extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
-extern int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1);
 extern char *xgetcwd(void);
 extern FILE *fopen_for_writing(const char *path);
 
index 86b2ff1e07614846465109dcb4b9afed11013839..2b8cdba157d9cd822acc88b7ec58cecda0149b85 100755 (executable)
@@ -46,6 +46,8 @@ git_commit_non_empty_tree()
 {
        if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
                map "$3"
+       elif test $# = 1 && test "$1" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904; then
+               :
        else
                git commit-tree "$@"
        fi
diff --git a/hash.h b/hash.h
new file mode 100644 (file)
index 0000000..f0d9ddd
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,14 @@
+#ifndef HASH_H
+#define HASH_H
+
+#if defined(SHA1_PPC)
+#include "ppc/sha1.h"
+#elif defined(SHA1_APPLE)
+#include <CommonCrypto/CommonDigest.h>
+#elif defined(SHA1_OPENSSL)
+#include <openssl/sha.h>
+#else /* SHA1_BLK */
+#include "block-sha1/sha1.h"
+#endif
+
+#endif
diff --git a/hex.c b/hex.c
index 845b01a874b45314a22e6000bb4e7762c3dd846a..eab7b626ee0c36a7cb48d251cfc2ea885461fe28 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -53,6 +53,14 @@ int get_oid_hex(const char *hex, struct object_id *oid)
        return get_sha1_hex(hex, oid->hash);
 }
 
+int parse_oid_hex(const char *hex, struct object_id *oid, const char **end)
+{
+       int ret = get_oid_hex(hex, oid);
+       if (!ret)
+               *end = hex + GIT_SHA1_HEXSZ;
+       return ret;
+}
+
 char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
 {
        static const char hex[] = "0123456789abcdef";
index b34b6ace7cd80a482eae16e2063aa9d8960e8729..ee049cb13df6ed6a64b1980004100a5dd55dde51 100644 (file)
@@ -168,6 +168,11 @@ static int is_alternate_allowed(const char *url)
        };
        int i;
 
+       if (http_follow_config != HTTP_FOLLOW_ALWAYS) {
+               warning("alternate disabled by http.followRedirects: %s", url);
+               return 0;
+       }
+
        for (i = 0; i < ARRAY_SIZE(protocols); i++) {
                const char *end;
                if (skip_prefix(url, protocols[i], &end) &&
@@ -296,13 +301,16 @@ static void process_alternates_response(void *callback_data)
                                        okay = 1;
                                }
                        }
-                       /* skip "objects\n" at end */
                        if (okay) {
                                struct strbuf target = STRBUF_INIT;
                                strbuf_add(&target, base, serverlen);
-                               strbuf_add(&target, data + i, posn - i - 7);
-
-                               if (is_alternate_allowed(target.buf)) {
+                               strbuf_add(&target, data + i, posn - i);
+                               if (!strbuf_strip_suffix(&target, "objects")) {
+                                       warning("ignoring alternate that does"
+                                               " not end in 'objects': %s",
+                                               target.buf);
+                                       strbuf_release(&target);
+                               } else if (is_alternate_allowed(target.buf)) {
                                        warning("adding alternate object store: %s",
                                                target.buf);
                                        newalt = xmalloc(sizeof(*newalt));
@@ -314,6 +322,8 @@ static void process_alternates_response(void *callback_data)
                                        while (tail->next != NULL)
                                                tail = tail->next;
                                        tail->next = newalt;
+                               } else {
+                                       strbuf_release(&target);
                                }
                        }
                }
@@ -331,9 +341,6 @@ static void fetch_alternates(struct walker *walker, const char *base)
        struct alternates_request alt_req;
        struct walker_data *cdata = walker->data;
 
-       if (http_follow_config != HTTP_FOLLOW_ALWAYS)
-               return;
-
        /*
         * If another request has already started fetching alternates,
         * wait for them to arrive and return to processing this request's
diff --git a/pager.c b/pager.c
index ae79643363091f4967097bdff6e009ea4c7df5e0..73ca8bc3b172e62dd4ee81a5490a9c3f067b3626 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -43,37 +43,6 @@ static int core_pager_config(const char *var, const char *value, void *data)
        return 0;
 }
 
-static void read_early_config(config_fn_t cb, void *data)
-{
-       git_config_with_options(cb, data, NULL, 1);
-
-       /*
-        * Note that this is a really dirty hack that does the wrong thing in
-        * many cases. The crux of the problem is that we cannot run
-        * setup_git_directory() early on in git's setup, so we have no idea if
-        * we are in a repository or not, and therefore are not sure whether
-        * and how to read repository-local config.
-        *
-        * So if we _aren't_ in a repository (or we are but we would reject its
-        * core.repositoryformatversion), we'll read whatever is in .git/config
-        * blindly. Similarly, if we _are_ in a repository, but not at the
-        * root, we'll fail to find .git/config (because it's really
-        * ../.git/config, etc). See t7006 for a complete set of failures.
-        *
-        * However, we have historically provided this hack because it does
-        * work some of the time (namely when you are at the top-level of a
-        * valid repository), and would rarely make things worse (i.e., you do
-        * not generally have a .git/config file sitting around).
-        */
-       if (!startup_info->have_repository) {
-               struct git_config_source repo_config;
-
-               memset(&repo_config, 0, sizeof(repo_config));
-               repo_config.file = ".git/config";
-               git_config_with_options(cb, data, &repo_config, 1);
-       }
-}
-
 const char *git_pager(int stdout_is_tty)
 {
        const char *pager;
index b961f00c8cd0eb68c56b33aa829579e8609edba6..303efda837a10ec4e8798f9899c90cd511752cfe 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "pathspec.h"
+#include "attr.h"
 
 /*
  * Finds which of the given pathspecs match items in the index.
@@ -72,6 +73,7 @@ static struct pathspec_magic {
        { PATHSPEC_GLOB,    '\0', "glob" },
        { PATHSPEC_ICASE,   '\0', "icase" },
        { PATHSPEC_EXCLUDE,  '!', "exclude" },
+       { PATHSPEC_ATTR,    '\0', "attr" },
 };
 
 static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
@@ -87,6 +89,116 @@ static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
        strbuf_addf(sb, ",prefix:%d)", prefixlen);
 }
 
+static size_t strcspn_escaped(const char *s, const char *stop)
+{
+       const char *i;
+
+       for (i = s; *i; i++) {
+               /* skip the escaped character */
+               if (i[0] == '\\' && i[1]) {
+                       i++;
+                       continue;
+               }
+
+               if (strchr(stop, *i))
+                       break;
+       }
+       return i - s;
+}
+
+static inline int invalid_value_char(const char ch)
+{
+       if (isalnum(ch) || strchr(",-_", ch))
+               return 0;
+       return -1;
+}
+
+static char *attr_value_unescape(const char *value)
+{
+       const char *src;
+       char *dst, *ret;
+
+       ret = xmallocz(strlen(value));
+       for (src = value, dst = ret; *src; src++, dst++) {
+               if (*src == '\\') {
+                       if (!src[1])
+                               die(_("Escape character '\\' not allowed as "
+                                     "last character in attr value"));
+                       src++;
+               }
+               if (invalid_value_char(*src))
+                       die("cannot use '%c' for value matching", *src);
+               *dst = *src;
+       }
+       *dst = '\0';
+       return ret;
+}
+
+static void parse_pathspec_attr_match(struct pathspec_item *item, const char *value)
+{
+       struct string_list_item *si;
+       struct string_list list = STRING_LIST_INIT_DUP;
+
+       if (item->attr_check || item->attr_match)
+               die(_("Only one 'attr:' specification is allowed."));
+
+       if (!value || !*value)
+               die(_("attr spec must not be empty"));
+
+       string_list_split(&list, value, ' ', -1);
+       string_list_remove_empty_items(&list, 0);
+
+       item->attr_check = attr_check_alloc();
+       item->attr_match = xcalloc(list.nr, sizeof(struct attr_match));
+
+       for_each_string_list_item(si, &list) {
+               size_t attr_len;
+               char *attr_name;
+               const struct git_attr *a;
+
+               int j = item->attr_match_nr++;
+               const char *attr = si->string;
+               struct attr_match *am = &item->attr_match[j];
+
+               switch (*attr) {
+               case '!':
+                       am->match_mode = MATCH_UNSPECIFIED;
+                       attr++;
+                       attr_len = strlen(attr);
+                       break;
+               case '-':
+                       am->match_mode = MATCH_UNSET;
+                       attr++;
+                       attr_len = strlen(attr);
+                       break;
+               default:
+                       attr_len = strcspn(attr, "=");
+                       if (attr[attr_len] != '=')
+                               am->match_mode = MATCH_SET;
+                       else {
+                               const char *v = &attr[attr_len + 1];
+                               am->match_mode = MATCH_VALUE;
+                               am->value = attr_value_unescape(v);
+                       }
+                       break;
+               }
+
+               attr_name = xmemdupz(attr, attr_len);
+               a = git_attr(attr_name);
+               if (!a)
+                       die(_("invalid attribute name %s"), attr_name);
+
+               attr_check_append(item->attr_check, a);
+
+               free(attr_name);
+       }
+
+       if (item->attr_check->nr != item->attr_match_nr)
+               die("BUG: should have same number of entries");
+
+       string_list_clear(&list, 0);
+}
+
 static inline int get_literal_global(void)
 {
        static int literal = -1;
@@ -164,13 +276,14 @@ static int get_global_magic(int element_magic)
  * returns the position in 'elem' after all magic has been parsed
  */
 static const char *parse_long_magic(unsigned *magic, int *prefix_len,
+                                   struct pathspec_item *item,
                                    const char *elem)
 {
        const char *pos;
        const char *nextat;
 
        for (pos = elem + 2; *pos && *pos != ')'; pos = nextat) {
-               size_t len = strcspn(pos, ",)");
+               size_t len = strcspn_escaped(pos, ",)");
                int i;
 
                if (pos[len] == ',')
@@ -189,6 +302,14 @@ static const char *parse_long_magic(unsigned *magic, int *prefix_len,
                        continue;
                }
 
+               if (starts_with(pos, "attr:")) {
+                       char *attr_body = xmemdupz(pos + 5, len - 5);
+                       parse_pathspec_attr_match(item, attr_body);
+                       *magic |= PATHSPEC_ATTR;
+                       free(attr_body);
+                       continue;
+               }
+
                for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
                        if (strlen(pathspec_magic[i].name) == len &&
                            !strncmp(pathspec_magic[i].name, pos, len)) {
@@ -252,13 +373,14 @@ static const char *parse_short_magic(unsigned *magic, const char *elem)
 }
 
 static const char *parse_element_magic(unsigned *magic, int *prefix_len,
+                                      struct pathspec_item *item,
                                       const char *elem)
 {
        if (elem[0] != ':' || get_literal_global())
                return elem; /* nothing to do */
        else if (elem[1] == '(')
                /* longhand */
-               return parse_long_magic(magic, prefix_len, elem);
+               return parse_long_magic(magic, prefix_len, item, elem);
        else
                /* shorthand */
                return parse_short_magic(magic, elem);
@@ -335,12 +457,17 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
        char *match;
        int pathspec_prefix = -1;
 
+       item->attr_check = NULL;
+       item->attr_match = NULL;
+       item->attr_match_nr = 0;
+
        /* PATHSPEC_LITERAL_PATH ignores magic */
        if (flags & PATHSPEC_LITERAL_PATH) {
                magic = PATHSPEC_LITERAL;
        } else {
                copyfrom = parse_element_magic(&element_magic,
                                               &pathspec_prefix,
+                                              item,
                                               elt);
                magic |= element_magic;
                magic |= get_global_magic(element_magic);
@@ -565,26 +692,46 @@ void parse_pathspec(struct pathspec *pathspec,
 
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
 {
-       int i;
+       int i, j;
 
        *dst = *src;
        ALLOC_ARRAY(dst->items, dst->nr);
        COPY_ARRAY(dst->items, src->items, dst->nr);
 
        for (i = 0; i < dst->nr; i++) {
-               dst->items[i].match = xstrdup(src->items[i].match);
-               dst->items[i].original = xstrdup(src->items[i].original);
+               struct pathspec_item *d = &dst->items[i];
+               struct pathspec_item *s = &src->items[i];
+
+               d->match = xstrdup(s->match);
+               d->original = xstrdup(s->original);
+
+               ALLOC_ARRAY(d->attr_match, d->attr_match_nr);
+               COPY_ARRAY(d->attr_match, s->attr_match, d->attr_match_nr);
+               for (j = 0; j < d->attr_match_nr; j++) {
+                       const char *value = s->attr_match[j].value;
+                       d->attr_match[j].value = xstrdup_or_null(value);
+               }
+
+               d->attr_check = attr_check_dup(s->attr_check);
        }
 }
 
 void clear_pathspec(struct pathspec *pathspec)
 {
-       int i;
+       int i, j;
 
        for (i = 0; i < pathspec->nr; i++) {
                free(pathspec->items[i].match);
                free(pathspec->items[i].original);
+
+               for (j = 0; j < pathspec->items[j].attr_match_nr; j++)
+                       free(pathspec->items[i].attr_match[j].value);
+               free(pathspec->items[i].attr_match);
+
+               if (pathspec->items[i].attr_check)
+                       attr_check_free(pathspec->items[i].attr_check);
        }
+
        free(pathspec->items);
        pathspec->items = NULL;
        pathspec->nr = 0;
index 49fd823ddfe9eb96c983f9f812f6ca1dcf062951..55e976972cecfa5f0c204266c5d2a3a4d3d306a0 100644 (file)
@@ -8,13 +8,15 @@
 #define PATHSPEC_GLOB          (1<<3)
 #define PATHSPEC_ICASE         (1<<4)
 #define PATHSPEC_EXCLUDE       (1<<5)
+#define PATHSPEC_ATTR          (1<<6)
 #define PATHSPEC_ALL_MAGIC       \
        (PATHSPEC_FROMTOP       | \
         PATHSPEC_MAXDEPTH      | \
         PATHSPEC_LITERAL       | \
         PATHSPEC_GLOB          | \
         PATHSPEC_ICASE         | \
-        PATHSPEC_EXCLUDE)
+        PATHSPEC_EXCLUDE       | \
+        PATHSPEC_ATTR)
 
 #define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
 
@@ -31,6 +33,17 @@ struct pathspec {
                int len, prefix;
                int nowildcard_len;
                int flags;
+               int attr_match_nr;
+               struct attr_match {
+                       char *value;
+                       enum attr_match_mode {
+                               MATCH_SET,
+                               MATCH_UNSET,
+                               MATCH_VALUE,
+                               MATCH_UNSPECIFIED
+                       } match_mode;
+               } *attr_match;
+               struct attr_check *attr_check;
        } *items;
 };
 
index d0199cace4aa0ab22a46fdb5ac2777c4e7e6153b..a8a979bd4fcbac6732d26dda3f826a1eec3d3a6b 100644 (file)
@@ -58,7 +58,7 @@ struct recent_data {
        unsigned long timestamp;
 };
 
-static void add_recent_object(const unsigned char *sha1,
+static void add_recent_object(const struct object_id *oid,
                              unsigned long mtime,
                              struct recent_data *data)
 {
@@ -75,37 +75,37 @@ static void add_recent_object(const unsigned char *sha1,
         * later processing, and the revision machinery expects
         * commits and tags to have been parsed.
         */
-       type = sha1_object_info(sha1, NULL);
+       type = sha1_object_info(oid->hash, NULL);
        if (type < 0)
-               die("unable to get object info for %s", sha1_to_hex(sha1));
+               die("unable to get object info for %s", oid_to_hex(oid));
 
        switch (type) {
        case OBJ_TAG:
        case OBJ_COMMIT:
-               obj = parse_object_or_die(sha1, NULL);
+               obj = parse_object_or_die(oid->hash, NULL);
                break;
        case OBJ_TREE:
-               obj = (struct object *)lookup_tree(sha1);
+               obj = (struct object *)lookup_tree(oid->hash);
                break;
        case OBJ_BLOB:
-               obj = (struct object *)lookup_blob(sha1);
+               obj = (struct object *)lookup_blob(oid->hash);
                break;
        default:
                die("unknown object type for %s: %s",
-                   sha1_to_hex(sha1), typename(type));
+                   oid_to_hex(oid), typename(type));
        }
 
        if (!obj)
-               die("unable to lookup %s", sha1_to_hex(sha1));
+               die("unable to lookup %s", oid_to_hex(oid));
 
        add_pending_object(data->revs, obj, "");
 }
 
-static int add_recent_loose(const unsigned char *sha1,
+static int add_recent_loose(const struct object_id *oid,
                            const char *path, void *data)
 {
        struct stat st;
-       struct object *obj = lookup_object(sha1);
+       struct object *obj = lookup_object(oid->hash);
 
        if (obj && obj->flags & SEEN)
                return 0;
@@ -119,22 +119,22 @@ static int add_recent_loose(const unsigned char *sha1,
                 */
                if (errno == ENOENT)
                        return 0;
-               return error_errno("unable to stat %s", sha1_to_hex(sha1));
+               return error_errno("unable to stat %s", oid_to_hex(oid));
        }
 
-       add_recent_object(sha1, st.st_mtime, data);
+       add_recent_object(oid, st.st_mtime, data);
        return 0;
 }
 
-static int add_recent_packed(const unsigned char *sha1,
+static int add_recent_packed(const struct object_id *oid,
                             struct packed_git *p, uint32_t pos,
                             void *data)
 {
-       struct object *obj = lookup_object(sha1);
+       struct object *obj = lookup_object(oid->hash);
 
        if (obj && obj->flags & SEEN)
                return 0;
-       add_recent_object(sha1, p->mtime, data);
+       add_recent_object(oid, p->mtime, data);
        return 0;
 }
 
index 9054369dd0c6ddfd35794fc63f0e0296ec0c9c91..e44775182392c5672fd6ec84eb954db339e5a1ea 100644 (file)
@@ -1558,10 +1558,27 @@ static void tweak_untracked_cache(struct index_state *istate)
        }
 }
 
+static void tweak_split_index(struct index_state *istate)
+{
+       switch (git_config_get_split_index()) {
+       case -1: /* unset: do nothing */
+               break;
+       case 0: /* false */
+               remove_split_index(istate);
+               break;
+       case 1: /* true */
+               add_split_index(istate);
+               break;
+       default: /* unknown value: do nothing */
+               break;
+       }
+}
+
 static void post_read_index_from(struct index_state *istate)
 {
        check_ce_order(istate);
        tweak_untracked_cache(istate);
+       tweak_split_index(istate);
 }
 
 /* remember to discard_cache() before reading a different cache! */
@@ -1657,10 +1674,25 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        die("index file corrupt");
 }
 
+/*
+ * Signal that the shared index is used by updating its mtime.
+ *
+ * This way, shared index can be removed if they have not been used
+ * for some time.
+ */
+static void freshen_shared_index(char *base_sha1_hex, int warn)
+{
+       const char *shared_index = git_path("sharedindex.%s", base_sha1_hex);
+       if (!check_and_freshen_file(shared_index, 1) && warn)
+               warning("could not freshen shared index '%s'", shared_index);
+}
+
 int read_index_from(struct index_state *istate, const char *path)
 {
        struct split_index *split_index;
        int ret;
+       char *base_sha1_hex;
+       const char *base_path;
 
        /* istate->initialized covers both .git/index and .git/sharedindex.xxx */
        if (istate->initialized)
@@ -1678,15 +1710,16 @@ int read_index_from(struct index_state *istate, const char *path)
                discard_index(split_index->base);
        else
                split_index->base = xcalloc(1, sizeof(*split_index->base));
-       ret = do_read_index(split_index->base,
-                           git_path("sharedindex.%s",
-                                    sha1_to_hex(split_index->base_sha1)), 1);
+
+       base_sha1_hex = sha1_to_hex(split_index->base_sha1);
+       base_path = git_path("sharedindex.%s", base_sha1_hex);
+       ret = do_read_index(split_index->base, base_path, 1);
        if (hashcmp(split_index->base_sha1, split_index->base->sha1))
                die("broken index, expect %s in %s, got %s",
-                   sha1_to_hex(split_index->base_sha1),
-                   git_path("sharedindex.%s",
-                            sha1_to_hex(split_index->base_sha1)),
+                   base_sha1_hex, base_path,
                    sha1_to_hex(split_index->base->sha1));
+
+       freshen_shared_index(base_sha1_hex, 0);
        merge_base_index(istate);
        post_read_index_from(istate);
        return ret;
@@ -2169,6 +2202,65 @@ static int write_split_index(struct index_state *istate,
        return ret;
 }
 
+static const char *shared_index_expire = "2.weeks.ago";
+
+static unsigned long get_shared_index_expire_date(void)
+{
+       static unsigned long shared_index_expire_date;
+       static int shared_index_expire_date_prepared;
+
+       if (!shared_index_expire_date_prepared) {
+               git_config_get_expiry("splitindex.sharedindexexpire",
+                                     &shared_index_expire);
+               shared_index_expire_date = approxidate(shared_index_expire);
+               shared_index_expire_date_prepared = 1;
+       }
+
+       return shared_index_expire_date;
+}
+
+static int should_delete_shared_index(const char *shared_index_path)
+{
+       struct stat st;
+       unsigned long expiration;
+
+       /* Check timestamp */
+       expiration = get_shared_index_expire_date();
+       if (!expiration)
+               return 0;
+       if (stat(shared_index_path, &st))
+               return error_errno(_("could not stat '%s"), shared_index_path);
+       if (st.st_mtime > expiration)
+               return 0;
+
+       return 1;
+}
+
+static int clean_shared_index_files(const char *current_hex)
+{
+       struct dirent *de;
+       DIR *dir = opendir(get_git_dir());
+
+       if (!dir)
+               return error_errno(_("unable to open git dir: %s"), get_git_dir());
+
+       while ((de = readdir(dir)) != NULL) {
+               const char *sha1_hex;
+               const char *shared_index_path;
+               if (!skip_prefix(de->d_name, "sharedindex.", &sha1_hex))
+                       continue;
+               if (!strcmp(sha1_hex, current_hex))
+                       continue;
+               shared_index_path = git_path("%s", de->d_name);
+               if (should_delete_shared_index(shared_index_path) > 0 &&
+                   unlink(shared_index_path))
+                       warning_errno(_("unable to unlink: %s"), shared_index_path);
+       }
+       closedir(dir);
+
+       return 0;
+}
+
 static struct tempfile temporary_sharedindex;
 
 static int write_shared_index(struct index_state *istate,
@@ -2190,14 +2282,48 @@ static int write_shared_index(struct index_state *istate,
        }
        ret = rename_tempfile(&temporary_sharedindex,
                              git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
-       if (!ret)
+       if (!ret) {
                hashcpy(si->base_sha1, si->base->sha1);
+               clean_shared_index_files(sha1_to_hex(si->base->sha1));
+       }
+
        return ret;
 }
 
+static const int default_max_percent_split_change = 20;
+
+static int too_many_not_shared_entries(struct index_state *istate)
+{
+       int i, not_shared = 0;
+       int max_split = git_config_get_max_percent_split_change();
+
+       switch (max_split) {
+       case -1:
+               /* not or badly configured: use the default value */
+               max_split = default_max_percent_split_change;
+               break;
+       case 0:
+               return 1; /* 0% means always write a new shared index */
+       case 100:
+               return 0; /* 100% means never write a new shared index */
+       default:
+               break; /* just use the configured value */
+       }
+
+       /* Count not shared entries */
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
+               if (!ce->index)
+                       not_shared++;
+       }
+
+       return (int64_t)istate->cache_nr * max_split < (int64_t)not_shared * 100;
+}
+
 int write_locked_index(struct index_state *istate, struct lock_file *lock,
                       unsigned flags)
 {
+       int new_shared_index, ret;
        struct split_index *si = istate->split_index;
 
        if (!si || alternate_index_output ||
@@ -2212,13 +2338,24 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
                if ((v & 15) < 6)
                        istate->cache_changed |= SPLIT_INDEX_ORDERED;
        }
-       if (istate->cache_changed & SPLIT_INDEX_ORDERED) {
-               int ret = write_shared_index(istate, lock, flags);
+       if (too_many_not_shared_entries(istate))
+               istate->cache_changed |= SPLIT_INDEX_ORDERED;
+
+       new_shared_index = istate->cache_changed & SPLIT_INDEX_ORDERED;
+
+       if (new_shared_index) {
+               ret = write_shared_index(istate, lock, flags);
                if (ret)
                        return ret;
        }
 
-       return write_split_index(istate, lock, flags);
+       ret = write_split_index(istate, lock, flags);
+
+       /* Freshen the shared index only if the split-index was written */
+       if (!ret && !new_shared_index)
+               freshen_shared_index(sha1_to_hex(si->base_sha1), 1);
+
+       return ret;
 }
 
 /*
index 1ec0fb8391ba6be716e76c688a0f9e2f0208adc2..9c82b5b9d632bbbe66332b6c54badb5bc88d28d8 100644 (file)
@@ -15,6 +15,7 @@
 #include "version.h"
 #include "trailer.h"
 #include "wt-status.h"
+#include "commit-slab.h"
 
 static struct ref_msg {
        const char *gone;
@@ -1297,9 +1298,9 @@ static void populate_value(struct ref_array_item *ref)
        ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
 
        if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
-               unsigned char unused1[20];
+               struct object_id unused1;
                ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
-                                            unused1, NULL);
+                                            unused1.hash, NULL);
                if (!ref->symref)
                        ref->symref = "";
        }
@@ -1470,10 +1471,22 @@ static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom
        *v = &ref->value[atom];
 }
 
+/*
+ * Unknown has to be "0" here, because that's the default value for
+ * contains_cache slab entries that have not yet been assigned.
+ */
 enum contains_result {
-       CONTAINS_UNKNOWN = -1,
-       CONTAINS_NO = 0,
-       CONTAINS_YES = 1
+       CONTAINS_UNKNOWN = 0,
+       CONTAINS_NO,
+       CONTAINS_YES
+};
+
+define_commit_slab(contains_cache, enum contains_result);
+
+struct ref_filter_cbdata {
+       struct ref_array *array;
+       struct ref_filter *filter;
+       struct contains_cache contains_cache;
 };
 
 /*
@@ -1504,24 +1517,24 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
  * Do not recurse to find out, though, but return -1 if inconclusive.
  */
 static enum contains_result contains_test(struct commit *candidate,
-                           const struct commit_list *want)
+                                         const struct commit_list *want,
+                                         struct contains_cache *cache)
 {
-       /* was it previously marked as containing a want commit? */
-       if (candidate->object.flags & TMP_MARK)
-               return 1;
-       /* or marked as not possibly containing a want commit? */
-       if (candidate->object.flags & UNINTERESTING)
-               return 0;
+       enum contains_result *cached = contains_cache_at(cache, candidate);
+
+       /* If we already have the answer cached, return that. */
+       if (*cached)
+               return *cached;
+
        /* or are we it? */
        if (in_commit_list(want, candidate)) {
-               candidate->object.flags |= TMP_MARK;
-               return 1;
+               *cached = CONTAINS_YES;
+               return CONTAINS_YES;
        }
 
-       if (parse_commit(candidate) < 0)
-               return 0;
-
-       return -1;
+       /* Otherwise, we don't know; prepare to recurse */
+       parse_commit_or_die(candidate);
+       return CONTAINS_UNKNOWN;
 }
 
 static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack)
@@ -1532,10 +1545,11 @@ static void push_to_contains_stack(struct commit *candidate, struct contains_sta
 }
 
 static enum contains_result contains_tag_algo(struct commit *candidate,
-               const struct commit_list *want)
+                                             const struct commit_list *want,
+                                             struct contains_cache *cache)
 {
        struct contains_stack contains_stack = { 0, 0, NULL };
-       int result = contains_test(candidate, want);
+       enum contains_result result = contains_test(candidate, want, cache);
 
        if (result != CONTAINS_UNKNOWN)
                return result;
@@ -1547,16 +1561,16 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                struct commit_list *parents = entry->parents;
 
                if (!parents) {
-                       commit->object.flags |= UNINTERESTING;
+                       *contains_cache_at(cache, commit) = CONTAINS_NO;
                        contains_stack.nr--;
                }
                /*
                 * If we just popped the stack, parents->item has been marked,
-                * therefore contains_test will return a meaningful 0 or 1.
+                * therefore contains_test will return a meaningful yes/no.
                 */
-               else switch (contains_test(parents->item, want)) {
+               else switch (contains_test(parents->item, want, cache)) {
                case CONTAINS_YES:
-                       commit->object.flags |= TMP_MARK;
+                       *contains_cache_at(cache, commit) = CONTAINS_YES;
                        contains_stack.nr--;
                        break;
                case CONTAINS_NO:
@@ -1568,13 +1582,14 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                }
        }
        free(contains_stack.contains_stack);
-       return contains_test(candidate, want);
+       return contains_test(candidate, want, cache);
 }
 
-static int commit_contains(struct ref_filter *filter, struct commit *commit)
+static int commit_contains(struct ref_filter *filter, struct commit *commit,
+                          struct contains_cache *cache)
 {
        if (filter->with_commit_tag_algo)
-               return contains_tag_algo(commit, filter->with_commit);
+               return contains_tag_algo(commit, filter->with_commit, cache) == CONTAINS_YES;
        return is_descendant_of(commit, filter->with_commit);
 }
 
@@ -1771,7 +1786,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
                        return 0;
                /* We perform the filtering for the '--contains' option */
                if (filter->with_commit &&
-                   !commit_contains(filter, commit))
+                   !commit_contains(filter, commit, &ref_cbdata->contains_cache))
                        return 0;
        }
 
@@ -1871,6 +1886,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
                broken = 1;
        filter->kind = type & FILTER_REFS_KIND_MASK;
 
+       init_contains_cache(&ref_cbdata.contains_cache);
+
        /*  Simple per-ref filtering */
        if (!filter->kind)
                die("filter_refs: invalid type");
@@ -1893,6 +1910,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
                        head_ref(ref_filter_handler, &ref_cbdata);
        }
 
+       clear_contains_cache(&ref_cbdata.contains_cache);
 
        /*  Filters that need revision walking */
        if (filter->merge_commit)
index 154e24c405fc9526ab1279b1c515271f0d2cfd7b..e738c5dfd364cf6e79ff48d5f154824fb94dc8a8 100644 (file)
@@ -71,11 +71,6 @@ struct ref_filter {
                verbose;
 };
 
-struct ref_filter_cbdata {
-       struct ref_array *array;
-       struct ref_filter *filter;
-};
-
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
        { OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \
index a246af27678a76d37c87bb56959ac050dff1f97f..99679f58255e386526335da8342e17356b48a4f9 100644 (file)
@@ -10,7 +10,7 @@ struct complete_reflogs {
        char *ref;
        const char *short_ref;
        struct reflog_info {
-               unsigned char osha1[20], nsha1[20];
+               struct object_id ooid, noid;
                char *email;
                unsigned long timestamp;
                int tz;
@@ -19,7 +19,7 @@ struct complete_reflogs {
        int nr, alloc;
 };
 
-static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1,
+static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
@@ -28,8 +28,8 @@ static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1,
 
        ALLOC_GROW(array->items, array->nr + 1, array->alloc);
        item = array->items + array->nr;
-       hashcpy(item->osha1, osha1);
-       hashcpy(item->nsha1, nsha1);
+       oidcpy(&item->ooid, ooid);
+       oidcpy(&item->noid, noid);
        item->email = xstrdup(email);
        item->timestamp = timestamp;
        item->tz = tz;
@@ -45,11 +45,11 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
        reflogs->ref = xstrdup(ref);
        for_each_reflog_ent(ref, read_one_reflog, reflogs);
        if (reflogs->nr == 0) {
-               unsigned char sha1[20];
+               struct object_id oid;
                const char *name;
                void *name_to_free;
                name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
-                                                    sha1, NULL);
+                                                    oid.hash, NULL);
                if (name) {
                        for_each_reflog_ent(name, read_one_reflog, reflogs);
                        free(name_to_free);
@@ -172,18 +172,18 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
                reflogs = item->util;
        else {
                if (*branch == '\0') {
-                       unsigned char sha1[20];
+                       struct object_id oid;
                        free(branch);
-                       branch = resolve_refdup("HEAD", 0, sha1, NULL);
+                       branch = resolve_refdup("HEAD", 0, oid.hash, NULL);
                        if (!branch)
                                die ("No current branch");
 
                }
                reflogs = read_complete_reflog(branch);
                if (!reflogs || reflogs->nr == 0) {
-                       unsigned char sha1[20];
+                       struct object_id oid;
                        char *b;
-                       if (dwim_log(branch, strlen(branch), sha1, &b) == 1) {
+                       if (dwim_log(branch, strlen(branch), oid.hash, &b) == 1) {
                                if (reflogs) {
                                        free(reflogs->ref);
                                        free(reflogs);
@@ -238,13 +238,13 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
        do {
                reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
                commit_reflog->recno--;
-               logobj = parse_object(reflog->osha1);
+               logobj = parse_object(reflog->ooid.hash);
        } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
 
-       if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->osha1)) {
+       if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->ooid.hash)) {
                /* a root commit, but there are still more entries to show */
                reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
-               logobj = parse_object(reflog->nsha1);
+               logobj = parse_object(reflog->noid.hash);
        }
 
        if (!logobj || logobj->type != OBJ_COMMIT) {
diff --git a/refs.c b/refs.c
index 4d6bf9237b5947c93dd366ccc1511f2a73649ae6..e7606716ddfff47b29f90e4d46cb2d59633cb455 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -405,7 +405,7 @@ int refname_match(const char *abbrev_name, const char *full_name)
 static char *substitute_branch_name(const char **string, int *len)
 {
        struct strbuf buf = STRBUF_INIT;
-       int ret = interpret_branch_name(*string, *len, &buf);
+       int ret = interpret_branch_name(*string, *len, &buf, 0);
 
        if (ret == *len) {
                size_t size;
@@ -675,7 +675,7 @@ struct read_ref_at_cb {
        int *cutoff_cnt;
 };
 
-static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
+static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
@@ -699,30 +699,30 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
                 * hold the values for the previous record.
                 */
                if (!is_null_sha1(cb->osha1)) {
-                       hashcpy(cb->sha1, nsha1);
-                       if (hashcmp(cb->osha1, nsha1))
+                       hashcpy(cb->sha1, noid->hash);
+                       if (hashcmp(cb->osha1, noid->hash))
                                warning("Log for ref %s has gap after %s.",
                                        cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
                }
                else if (cb->date == cb->at_time)
-                       hashcpy(cb->sha1, nsha1);
-               else if (hashcmp(nsha1, cb->sha1))
+                       hashcpy(cb->sha1, noid->hash);
+               else if (hashcmp(noid->hash, cb->sha1))
                        warning("Log for ref %s unexpectedly ended on %s.",
                                cb->refname, show_date(cb->date, cb->tz,
                                                       DATE_MODE(RFC2822)));
-               hashcpy(cb->osha1, osha1);
-               hashcpy(cb->nsha1, nsha1);
+               hashcpy(cb->osha1, ooid->hash);
+               hashcpy(cb->nsha1, noid->hash);
                cb->found_it = 1;
                return 1;
        }
-       hashcpy(cb->osha1, osha1);
-       hashcpy(cb->nsha1, nsha1);
+       hashcpy(cb->osha1, ooid->hash);
+       hashcpy(cb->nsha1, noid->hash);
        if (cb->cnt > 0)
                cb->cnt--;
        return 0;
 }
 
-static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
+static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
                                  const char *email, unsigned long timestamp,
                                  int tz, const char *message, void *cb_data)
 {
@@ -736,9 +736,9 @@ static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
                *cb->cutoff_tz = tz;
        if (cb->cutoff_cnt)
                *cb->cutoff_cnt = cb->reccnt;
-       hashcpy(cb->sha1, osha1);
+       hashcpy(cb->sha1, ooid->hash);
        if (is_null_sha1(cb->sha1))
-               hashcpy(cb->sha1, nsha1);
+               hashcpy(cb->sha1, noid->hash);
        /* We just want the first entry */
        return 1;
 }
diff --git a/refs.h b/refs.h
index e529f4c3a88a79488df4b610b65a1c9d9c18c4a0..3df0d45ebb6bb5900254edd40383d9de8e539a31 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -292,7 +292,7 @@ int delete_reflog(const char *refname);
 
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(
-               unsigned char *old_sha1, unsigned char *new_sha1,
+               struct object_id *old_oid, struct object_id *new_oid,
                const char *committer, unsigned long timestamp,
                int tz, const char *msg, void *cb_data);
 
index b42df147c9c4335b0e47201f751e7e05204e4b01..50188e92f9fd90ca52d8257f3618678d632eb38a 100644 (file)
@@ -3102,16 +3102,17 @@ static int files_delete_reflog(struct ref_store *ref_store,
 
 static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
 {
-       unsigned char osha1[20], nsha1[20];
+       struct object_id ooid, noid;
        char *email_end, *message;
        unsigned long timestamp;
        int tz;
+       const char *p = sb->buf;
 
        /* old SP new SP name <email> SP time TAB msg LF */
-       if (sb->len < 83 || sb->buf[sb->len - 1] != '\n' ||
-           get_sha1_hex(sb->buf, osha1) || sb->buf[40] != ' ' ||
-           get_sha1_hex(sb->buf + 41, nsha1) || sb->buf[81] != ' ' ||
-           !(email_end = strchr(sb->buf + 82, '>')) ||
+       if (!sb->len || sb->buf[sb->len - 1] != '\n' ||
+           parse_oid_hex(p, &ooid, &p) || *p++ != ' ' ||
+           parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
+           !(email_end = strchr(p, '>')) ||
            email_end[1] != ' ' ||
            !(timestamp = strtoul(email_end + 2, &message, 10)) ||
            !message || message[0] != ' ' ||
@@ -3125,7 +3126,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
                message += 6;
        else
                message += 7;
-       return fn(osha1, nsha1, sb->buf + 82, timestamp, tz, message, cb_data);
+       return fn(&ooid, &noid, p, timestamp, tz, message, cb_data);
 }
 
 static char *find_beginning_of_line(char *bob, char *scan)
@@ -3954,10 +3955,10 @@ struct expire_reflog_cb {
        reflog_expiry_should_prune_fn *should_prune_fn;
        void *policy_cb;
        FILE *newlog;
-       unsigned char last_kept_sha1[20];
+       struct object_id last_kept_oid;
 };
 
-static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
                             const char *email, unsigned long timestamp, int tz,
                             const char *message, void *cb_data)
 {
@@ -3965,9 +3966,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
        struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
 
        if (cb->flags & EXPIRE_REFLOGS_REWRITE)
-               osha1 = cb->last_kept_sha1;
+               ooid = &cb->last_kept_oid;
 
-       if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz,
+       if ((*cb->should_prune_fn)(ooid->hash, noid->hash, email, timestamp, tz,
                                   message, policy_cb)) {
                if (!cb->newlog)
                        printf("would prune %s", message);
@@ -3976,9 +3977,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
        } else {
                if (cb->newlog) {
                        fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
-                               sha1_to_hex(osha1), sha1_to_hex(nsha1),
+                               oid_to_hex(ooid), oid_to_hex(noid),
                                email, timestamp, tz, message);
-                       hashcpy(cb->last_kept_sha1, nsha1);
+                       oidcpy(&cb->last_kept_oid, noid);
                }
                if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
                        printf("keep %s", message);
@@ -4065,14 +4066,14 @@ static int files_reflog_expire(struct ref_store *ref_store,
                 */
                int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) &&
                        !(type & REF_ISSYMREF) &&
-                       !is_null_sha1(cb.last_kept_sha1);
+                       !is_null_oid(&cb.last_kept_oid);
 
                if (close_lock_file(&reflog_lock)) {
                        status |= error("couldn't write %s: %s", log_file,
                                        strerror(errno));
                } else if (update &&
                           (write_in_full(get_lock_file_fd(lock->lk),
-                               sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
+                               oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
                            write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
                            close_ref(lock) < 0)) {
                        status |= error("couldn't write %s",
index a5bbbe0ef965c5e62be50e8ee0c03794e393cc8c..dd8c5175776baabbac2bd735e21bc859fb62a111 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -89,8 +89,13 @@ struct ref {
                force:1,
                forced_update:1,
                expect_old_sha1:1,
-               deletion:1,
-               matched:1;
+               deletion:1;
+
+       enum {
+               REF_NOT_MATCHED = 0, /* initial value */
+               REF_MATCHED,
+               REF_UNADVERTISED_NOT_ALLOWED
+       } match_status;
 
        /*
         * Order is important here, as we write to FETCH_HEAD
index b37dbec378f3ae1a33d00d591c1042c609e45fa4..7ff61ff5f73db07af522ac57d62ed9e699aedf72 100644 (file)
@@ -147,7 +147,7 @@ static void add_pending_object_with_path(struct rev_info *revs,
                revs->no_walk = 0;
        if (revs->reflog_info && obj->type == OBJ_COMMIT) {
                struct strbuf buf = STRBUF_INIT;
-               int len = interpret_branch_name(name, 0, &buf);
+               int len = interpret_branch_name(name, 0, &buf, 0);
                int st;
 
                if (0 < len && name[len] && buf.len)
@@ -1196,11 +1196,11 @@ static void handle_refs(const char *submodule, struct rev_info *revs, unsigned f
        for_each(submodule, handle_one_ref, &cb);
 }
 
-static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
+static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
 {
        struct all_refs_cb *cb = cb_data;
-       if (!is_null_sha1(sha1)) {
-               struct object *o = parse_object(sha1);
+       if (!is_null_oid(oid)) {
+               struct object *o = parse_object(oid->hash);
                if (o) {
                        o->flags |= cb->all_flags;
                        /* ??? CMDLINEFLAGS ??? */
@@ -1214,12 +1214,12 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
        }
 }
 
-static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
-       handle_one_reflog_commit(osha1, cb_data);
-       handle_one_reflog_commit(nsha1, cb_data);
+       handle_one_reflog_commit(ooid, cb_data);
+       handle_one_reflog_commit(noid, cb_data);
        return 0;
 }
 
index 6195b43e9abacf346f52b6bbb758c9ca7b617d4e..d2d2a49a0231293e98ab8546945f8d3ef9fa205d 100644 (file)
@@ -72,6 +72,7 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
        struct child_process po = CHILD_PROCESS_INIT;
        FILE *po_in;
        int i;
+       int rc;
 
        i = 4;
        if (args->use_thin_pack)
@@ -125,27 +126,44 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
                po.out = -1;
        }
 
-       if (finish_command(&po))
+       rc = finish_command(&po);
+       if (rc) {
+               /*
+                * For a normal non-zero exit, we assume pack-objects wrote
+                * something useful to stderr. For death by signal, though,
+                * we should mention it to the user. The exception is SIGPIPE
+                * (141), because that's a normal occurence if the remote end
+                * hangs up (and we'll report that by trying to read the unpack
+                * status).
+                */
+               if (rc > 128 && rc != 141)
+                       error("pack-objects died of signal %d", rc - 128);
                return -1;
+       }
+       return 0;
+}
+
+static int receive_unpack_status(int in)
+{
+       const char *line = packet_read_line(in, NULL);
+       if (!skip_prefix(line, "unpack ", &line))
+               return error(_("unable to parse remote unpack status: %s"), line);
+       if (strcmp(line, "ok"))
+               return error(_("remote unpack failed: %s"), line);
        return 0;
 }
 
 static int receive_status(int in, struct ref *refs)
 {
        struct ref *hint;
-       int ret = 0;
-       char *line = packet_read_line(in, NULL);
-       if (!starts_with(line, "unpack "))
-               return error("did not receive remote status");
-       if (strcmp(line, "unpack ok")) {
-               error("unpack failed: %s", line + 7);
-               ret = -1;
-       }
+       int ret;
+
        hint = NULL;
+       ret = receive_unpack_status(in);
        while (1) {
                char *refname;
                char *msg;
-               line = packet_read_line(in, NULL);
+               char *line = packet_read_line(in, NULL);
                if (!line)
                        break;
                if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) {
@@ -557,6 +575,14 @@ int send_pack(struct send_pack_args *args,
                                close(out);
                        if (git_connection_is_socket(conn))
                                shutdown(fd[0], SHUT_WR);
+
+                       /*
+                        * Do not even bother with the return value; we know we
+                        * are failing, and just want the error() side effects.
+                        */
+                       if (status_report)
+                               receive_unpack_status(in);
+
                        if (use_sideband) {
                                close(demux.out);
                                finish_async(&demux);
index 1f729b053bbc6f544fe0b3e8736113082894b24d..d76dc9cb2b07227d311e4a667aaa5f0d305da45d 100644 (file)
@@ -1997,7 +1997,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        if (item->command == TODO_EDIT) {
                                struct commit *commit = item->commit;
                                if (!res)
-                                       warning(_("stopped at %s... %.*s"),
+                                       fprintf(stderr,
+                                               _("Stopped at %s...  %.*s\n"),
                                                short_commit_name(commit),
                                                item->arg_len, item->arg);
                                return error_with_patch(commit,
diff --git a/setup.c b/setup.c
index 8f64fbdfb28fc2e487cfdba76561e5b3a25766f0..64f922a9378cc48c72b9173e7c45296bdb885678 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -531,6 +531,7 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
        ssize_t len;
 
        if (stat(path, &st)) {
+               /* NEEDSWORK: discern between ENOENT vs other errors */
                error_code = READ_GITFILE_ERR_STAT_FAILED;
                goto cleanup_return;
        }
@@ -721,8 +722,10 @@ static const char *setup_discovered_git_dir(const char *gitdir,
        if (offset == cwd->len)
                return NULL;
 
-       /* Make "offset" point to past the '/', and add a '/' at the end */
-       offset++;
+       /* Make "offset" point past the '/' (already the case for root dirs) */
+       if (offset != offset_1st_component(cwd->buf))
+               offset++;
+       /* Add a '/' at the end */
        strbuf_addch(cwd, '/');
        return cwd->buf + offset;
 }
@@ -816,50 +819,51 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
        }
 }
 
+enum discovery_result {
+       GIT_DIR_NONE = 0,
+       GIT_DIR_EXPLICIT,
+       GIT_DIR_DISCOVERED,
+       GIT_DIR_BARE,
+       /* these are errors */
+       GIT_DIR_HIT_CEILING = -1,
+       GIT_DIR_HIT_MOUNT_POINT = -2,
+       GIT_DIR_INVALID_GITFILE = -3
+};
+
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
+ *
+ * Also, we avoid changing any global state (such as the current working
+ * directory) to allow early callers.
+ *
+ * The directory where the search should start needs to be passed in via the
+ * `dir` parameter; upon return, the `dir` buffer will contain the path of
+ * the directory where the search ended, and `gitdir` will contain the path of
+ * the discovered .git/ directory, if any. If `gitdir` is not absolute, it
+ * is relative to `dir` (i.e. *not* necessarily the cwd).
  */
-static const char *setup_git_directory_gently_1(int *nongit_ok)
+static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
+                                                         struct strbuf *gitdir,
+                                                         int die_on_error)
 {
        const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
        struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
-       static struct strbuf cwd = STRBUF_INIT;
-       const char *gitdirenv, *ret;
-       char *gitfile;
-       int offset, offset_parent, ceil_offset = -1;
+       const char *gitdirenv;
+       int ceil_offset = -1, min_offset = has_dos_drive_prefix(dir->buf) ? 3 : 1;
        dev_t current_device = 0;
        int one_filesystem = 1;
 
-       /*
-        * We may have read an incomplete configuration before
-        * setting-up the git directory. If so, clear the cache so
-        * that the next queries to the configuration reload complete
-        * configuration (including the per-repo config file that we
-        * ignored previously).
-        */
-       git_config_clear();
-
-       /*
-        * Let's assume that we are in a git repository.
-        * If it turns out later that we are somewhere else, the value will be
-        * updated accordingly.
-        */
-       if (nongit_ok)
-               *nongit_ok = 0;
-
-       if (strbuf_getcwd(&cwd))
-               die_errno(_("Unable to read current working directory"));
-       offset = cwd.len;
-
        /*
         * If GIT_DIR is set explicitly, we're not going
         * to do any discovery, but we still do repository
         * validation.
         */
        gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-       if (gitdirenv)
-               return setup_explicit_git_dir(gitdirenv, &cwd, nongit_ok);
+       if (gitdirenv) {
+               strbuf_addstr(gitdir, gitdirenv);
+               return GIT_DIR_EXPLICIT;
+       }
 
        if (env_ceiling_dirs) {
                int empty_entry_found = 0;
@@ -867,15 +871,15 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
                string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
                filter_string_list(&ceiling_dirs, 0,
                                   canonicalize_ceiling_entry, &empty_entry_found);
-               ceil_offset = longest_ancestor_length(cwd.buf, &ceiling_dirs);
+               ceil_offset = longest_ancestor_length(dir->buf, &ceiling_dirs);
                string_list_clear(&ceiling_dirs, 0);
        }
 
-       if (ceil_offset < 0 && has_dos_drive_prefix(cwd.buf))
-               ceil_offset = 1;
+       if (ceil_offset < 0)
+               ceil_offset = min_offset - 2;
 
        /*
-        * Test in the following order (relative to the cwd):
+        * Test in the following order (relative to the dir):
         * - .git (file containing "gitdir: <path>")
         * - .git/
         * - ./ (bare)
@@ -887,61 +891,155 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
         */
        one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
        if (one_filesystem)
-               current_device = get_device_or_die(".", NULL, 0);
+               current_device = get_device_or_die(dir->buf, NULL, 0);
        for (;;) {
-               gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
-               if (gitfile)
-                       gitdirenv = gitfile = xstrdup(gitfile);
-               else {
-                       if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
-                               gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
+               int offset = dir->len, error_code = 0;
+
+               if (offset > min_offset)
+                       strbuf_addch(dir, '/');
+               strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
+               gitdirenv = read_gitfile_gently(dir->buf, die_on_error ?
+                                               NULL : &error_code);
+               if (!gitdirenv) {
+                       if (die_on_error ||
+                           error_code == READ_GITFILE_ERR_NOT_A_FILE) {
+                               /* NEEDSWORK: fail if .git is not file nor dir */
+                               if (is_git_directory(dir->buf))
+                                       gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
+                       } else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
+                               return GIT_DIR_INVALID_GITFILE;
                }
-
+               strbuf_setlen(dir, offset);
                if (gitdirenv) {
-                       ret = setup_discovered_git_dir(gitdirenv,
-                                                      &cwd, offset,
-                                                      nongit_ok);
-                       free(gitfile);
-                       return ret;
+                       strbuf_addstr(gitdir, gitdirenv);
+                       return GIT_DIR_DISCOVERED;
                }
-               free(gitfile);
 
-               if (is_git_directory("."))
-                       return setup_bare_git_dir(&cwd, offset, nongit_ok);
-
-               offset_parent = offset;
-               while (--offset_parent > ceil_offset && cwd.buf[offset_parent] != '/');
-               if (offset_parent <= ceil_offset)
-                       return setup_nongit(cwd.buf, nongit_ok);
-               if (one_filesystem) {
-                       dev_t parent_device = get_device_or_die("..", cwd.buf,
-                                                               offset);
-                       if (parent_device != current_device) {
-                               if (nongit_ok) {
-                                       if (chdir(cwd.buf))
-                                               die_errno(_("Cannot come back to cwd"));
-                                       *nongit_ok = 1;
-                                       return NULL;
-                               }
-                               strbuf_setlen(&cwd, offset);
-                               die(_("Not a git repository (or any parent up to mount point %s)\n"
-                               "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
-                                   cwd.buf);
-                       }
-               }
-               if (chdir("..")) {
-                       strbuf_setlen(&cwd, offset);
-                       die_errno(_("Cannot change to '%s/..'"), cwd.buf);
+               if (is_git_directory(dir->buf)) {
+                       strbuf_addstr(gitdir, ".");
+                       return GIT_DIR_BARE;
                }
-               offset = offset_parent;
+
+               if (offset <= min_offset)
+                       return GIT_DIR_HIT_CEILING;
+
+               while (--offset > ceil_offset && !is_dir_sep(dir->buf[offset]))
+                       ; /* continue */
+               if (offset <= ceil_offset)
+                       return GIT_DIR_HIT_CEILING;
+
+               strbuf_setlen(dir, offset > min_offset ?  offset : min_offset);
+               if (one_filesystem &&
+                   current_device != get_device_or_die(dir->buf, NULL, offset))
+                       return GIT_DIR_HIT_MOUNT_POINT;
        }
 }
 
+const char *discover_git_directory(struct strbuf *gitdir)
+{
+       struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
+       size_t gitdir_offset = gitdir->len, cwd_len;
+       struct repository_format candidate;
+
+       if (strbuf_getcwd(&dir))
+               return NULL;
+
+       cwd_len = dir.len;
+       if (setup_git_directory_gently_1(&dir, gitdir, 0) <= 0) {
+               strbuf_release(&dir);
+               return NULL;
+       }
+
+       /*
+        * The returned gitdir is relative to dir, and if dir does not reflect
+        * the current working directory, we simply make the gitdir absolute.
+        */
+       if (dir.len < cwd_len && !is_absolute_path(gitdir->buf + gitdir_offset)) {
+               /* Avoid a trailing "/." */
+               if (!strcmp(".", gitdir->buf + gitdir_offset))
+                       strbuf_setlen(gitdir, gitdir_offset);
+               else
+                       strbuf_addch(&dir, '/');
+               strbuf_insert(gitdir, gitdir_offset, dir.buf, dir.len);
+       }
+
+       strbuf_reset(&dir);
+       strbuf_addf(&dir, "%s/config", gitdir->buf + gitdir_offset);
+       read_repository_format(&candidate, dir.buf);
+       strbuf_release(&dir);
+
+       if (verify_repository_format(&candidate, &err) < 0) {
+               warning("ignoring git dir '%s': %s",
+                       gitdir->buf + gitdir_offset, err.buf);
+               strbuf_release(&err);
+               return NULL;
+       }
+
+       return gitdir->buf + gitdir_offset;
+}
+
 const char *setup_git_directory_gently(int *nongit_ok)
 {
+       static struct strbuf cwd = STRBUF_INIT;
+       struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
        const char *prefix;
 
-       prefix = setup_git_directory_gently_1(nongit_ok);
+       /*
+        * We may have read an incomplete configuration before
+        * setting-up the git directory. If so, clear the cache so
+        * that the next queries to the configuration reload complete
+        * configuration (including the per-repo config file that we
+        * ignored previously).
+        */
+       git_config_clear();
+
+       /*
+        * Let's assume that we are in a git repository.
+        * If it turns out later that we are somewhere else, the value will be
+        * updated accordingly.
+        */
+       if (nongit_ok)
+               *nongit_ok = 0;
+
+       if (strbuf_getcwd(&cwd))
+               die_errno(_("Unable to read current working directory"));
+       strbuf_addbuf(&dir, &cwd);
+
+       switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) {
+       case GIT_DIR_NONE:
+               prefix = NULL;
+               break;
+       case GIT_DIR_EXPLICIT:
+               prefix = setup_explicit_git_dir(gitdir.buf, &cwd, nongit_ok);
+               break;
+       case GIT_DIR_DISCOVERED:
+               if (dir.len < cwd.len && chdir(dir.buf))
+                       die(_("Cannot change to '%s'"), dir.buf);
+               prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
+                                                 nongit_ok);
+               break;
+       case GIT_DIR_BARE:
+               if (dir.len < cwd.len && chdir(dir.buf))
+                       die(_("Cannot change to '%s'"), dir.buf);
+               prefix = setup_bare_git_dir(&cwd, dir.len, nongit_ok);
+               break;
+       case GIT_DIR_HIT_CEILING:
+               prefix = setup_nongit(cwd.buf, nongit_ok);
+               break;
+       case GIT_DIR_HIT_MOUNT_POINT:
+               if (nongit_ok) {
+                       *nongit_ok = 1;
+                       strbuf_release(&cwd);
+                       strbuf_release(&dir);
+                       return NULL;
+               }
+               die(_("Not a git repository (or any parent up to mount point %s)\n"
+                     "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
+                   dir.buf);
+       default:
+               die("BUG: unhandled setup_git_directory_1() result");
+       }
+
        if (prefix)
                setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
        else
@@ -950,6 +1048,9 @@ const char *setup_git_directory_gently(int *nongit_ok)
        startup_info->have_repository = !nongit_ok || !*nongit_ok;
        startup_info->prefix = prefix;
 
+       strbuf_release(&dir);
+       strbuf_release(&gitdir);
+
        return prefix;
 }
 
index 2ee3c617a6b45bc0dd52b634bbecec4d011a74bf..71063890ffe9cfe33f876dd8b2306458323c6f30 100644 (file)
@@ -277,31 +277,26 @@ static const char *alt_sha1_path(struct alternate_object_database *alt,
        return buf->buf;
 }
 
-/*
- * Return the name of the pack or index file with the specified sha1
- * in its filename.  *base and *name are scratch space that must be
- * provided by the caller.  which should be "pack" or "idx".
- */
-static char *sha1_get_pack_name(const unsigned char *sha1,
-                               struct strbuf *buf,
-                               const char *which)
+ char *odb_pack_name(struct strbuf *buf,
+                    const unsigned char *sha1,
+                    const char *ext)
 {
        strbuf_reset(buf);
        strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
-                   sha1_to_hex(sha1), which);
+                   sha1_to_hex(sha1), ext);
        return buf->buf;
 }
 
 char *sha1_pack_name(const unsigned char *sha1)
 {
        static struct strbuf buf = STRBUF_INIT;
-       return sha1_get_pack_name(sha1, &buf, "pack");
+       return odb_pack_name(&buf, sha1, "pack");
 }
 
 char *sha1_pack_index_name(const unsigned char *sha1)
 {
        static struct strbuf buf = STRBUF_INIT;
-       return sha1_get_pack_name(sha1, &buf, "idx");
+       return odb_pack_name(&buf, sha1, "idx");
 }
 
 struct alternate_object_database *alt_odb_list;
@@ -667,7 +662,7 @@ static int freshen_file(const char *fn)
  * either does not exist on disk, or has a stale mtime and may be subject to
  * pruning).
  */
-static int check_and_freshen_file(const char *fn, int freshen)
+int check_and_freshen_file(const char *fn, int freshen)
 {
        if (access(fn, F_OK))
                return 0;
@@ -2706,6 +2701,17 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p,
        }
 }
 
+const struct object_id *nth_packed_object_oid(struct object_id *oid,
+                                             struct packed_git *p,
+                                             uint32_t n)
+{
+       const unsigned char *hash = nth_packed_object_sha1(p, n);
+       if (!hash)
+               return NULL;
+       hashcpy(oid->hash, hash);
+       return oid;
+}
+
 void check_pack_index_ptr(const struct packed_git *p, const void *vptr)
 {
        const unsigned char *ptr = vptr;
@@ -3752,15 +3758,15 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
                strbuf_setlen(path, baselen);
                strbuf_addf(path, "/%s", de->d_name);
 
-               if (strlen(de->d_name) == 38)  {
-                       char hex[41];
-                       unsigned char sha1[20];
+               if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
+                       char hex[GIT_SHA1_HEXSZ+1];
+                       struct object_id oid;
 
                        snprintf(hex, sizeof(hex), "%02x%s",
                                 subdir_nr, de->d_name);
-                       if (!get_sha1_hex(hex, sha1)) {
+                       if (!get_oid_hex(hex, &oid)) {
                                if (obj_cb) {
-                                       r = obj_cb(sha1, path->buf, data);
+                                       r = obj_cb(&oid, path->buf, data);
                                        if (r)
                                                break;
                                }
@@ -3866,13 +3872,13 @@ static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn c
        int r = 0;
 
        for (i = 0; i < p->num_objects; i++) {
-               const unsigned char *sha1 = nth_packed_object_sha1(p, i);
+               struct object_id oid;
 
-               if (!sha1)
+               if (!nth_packed_object_oid(&oid, p, i))
                        return error("unable to get sha1 of object %u in %s",
                                     i, p->pack_name);
 
-               r = cb(sha1, p, i, data);
+               r = cb(&oid, p, i, data);
                if (r)
                        break;
        }
index 73a915ff1b3278f08ef4f327a55fe61d238f720a..cda9e49b12e7128dd22a8ccbaff437cb7db34d92 100644 (file)
@@ -1051,7 +1051,7 @@ struct grab_nth_branch_switch_cbdata {
        struct strbuf buf;
 };
 
-static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
                                  const char *email, unsigned long timestamp, int tz,
                                  const char *message, void *cb_data)
 {
@@ -1176,7 +1176,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str
        return 1;
 }
 
-static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
+static int reinterpret(const char *name, int namelen, int len,
+                      struct strbuf *buf, unsigned allowed)
 {
        /* we have extra data, which might need further processing */
        struct strbuf tmp = STRBUF_INIT;
@@ -1184,7 +1185,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu
        int ret;
 
        strbuf_add(buf, name + len, namelen - len);
-       ret = interpret_branch_name(buf->buf, buf->len, &tmp);
+       ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed);
        /* that data was not interpreted, remove our cruft */
        if (ret < 0) {
                strbuf_setlen(buf, used);
@@ -1205,11 +1206,27 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref)
        free(s);
 }
 
+static int branch_interpret_allowed(const char *refname, unsigned allowed)
+{
+       if (!allowed)
+               return 1;
+
+       if ((allowed & INTERPRET_BRANCH_LOCAL) &&
+           starts_with(refname, "refs/heads/"))
+               return 1;
+       if ((allowed & INTERPRET_BRANCH_REMOTE) &&
+           starts_with(refname, "refs/remotes/"))
+               return 1;
+
+       return 0;
+}
+
 static int interpret_branch_mark(const char *name, int namelen,
                                 int at, struct strbuf *buf,
                                 int (*get_mark)(const char *, int),
                                 const char *(*get_data)(struct branch *,
-                                                        struct strbuf *))
+                                                        struct strbuf *),
+                                unsigned allowed)
 {
        int len;
        struct branch *branch;
@@ -1234,64 +1251,55 @@ static int interpret_branch_mark(const char *name, int namelen,
        if (!value)
                die("%s", err.buf);
 
+       if (!branch_interpret_allowed(value, allowed))
+               return -1;
+
        set_shortened_ref(buf, value);
        return len + at;
 }
 
-/*
- * This reads short-hand syntax that not only evaluates to a commit
- * object name, but also can act as if the end user spelled the name
- * of the branch from the command line.
- *
- * - "@{-N}" finds the name of the Nth previous branch we were on, and
- *   places the name of the branch in the given buf and returns the
- *   number of characters parsed if successful.
- *
- * - "<branch>@{upstream}" finds the name of the other ref that
- *   <branch> is configured to merge with (missing <branch> defaults
- *   to the current branch), and places the name of the branch in the
- *   given buf and returns the number of characters parsed if
- *   successful.
- *
- * If the input is not of the accepted format, it returns a negative
- * number to signal an error.
- *
- * If the input was ok but there are not N branch switches in the
- * reflog, it returns 0.
- */
-int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
+int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
+                         unsigned allowed)
 {
        char *at;
        const char *start;
-       int len = interpret_nth_prior_checkout(name, namelen, buf);
+       int len;
 
        if (!namelen)
                namelen = strlen(name);
 
-       if (!len) {
-               return len; /* syntax Ok, not enough switches */
-       } else if (len > 0) {
-               if (len == namelen)
-                       return len; /* consumed all */
-               else
-                       return reinterpret(name, namelen, len, buf);
+       if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
+               len = interpret_nth_prior_checkout(name, namelen, buf);
+               if (!len) {
+                       return len; /* syntax Ok, not enough switches */
+               } else if (len > 0) {
+                       if (len == namelen)
+                               return len; /* consumed all */
+                       else
+                               return reinterpret(name, namelen, len, buf, allowed);
+               }
        }
 
        for (start = name;
             (at = memchr(start, '@', namelen - (start - name)));
             start = at + 1) {
 
-               len = interpret_empty_at(name, namelen, at - name, buf);
-               if (len > 0)
-                       return reinterpret(name, namelen, len, buf);
+               if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
+                       len = interpret_empty_at(name, namelen, at - name, buf);
+                       if (len > 0)
+                               return reinterpret(name, namelen, len, buf,
+                                                  allowed);
+               }
 
                len = interpret_branch_mark(name, namelen, at - name, buf,
-                                           upstream_mark, branch_get_upstream);
+                                           upstream_mark, branch_get_upstream,
+                                           allowed);
                if (len > 0)
                        return len;
 
                len = interpret_branch_mark(name, namelen, at - name, buf,
-                                           push_mark, branch_get_push);
+                                           push_mark, branch_get_push,
+                                           allowed);
                if (len > 0)
                        return len;
        }
@@ -1299,22 +1307,19 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
        return -1;
 }
 
-int strbuf_branchname(struct strbuf *sb, const char *name)
+void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed)
 {
        int len = strlen(name);
-       int used = interpret_branch_name(name, len, sb);
+       int used = interpret_branch_name(name, len, sb, allowed);
 
-       if (used == len)
-               return 0;
        if (used < 0)
                used = 0;
        strbuf_add(sb, name + used, len - used);
-       return len;
 }
 
 int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
 {
-       strbuf_branchname(sb, name);
+       strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
        if (name[0] == '-')
                return -1;
        strbuf_splice(sb, 0, 0, "refs/heads/", 11);
index 615f4cac05f7d381b3c31831b6fd9e8a72e1f982..f519e60f87578386ec9b4ea764bf530be0f64836 100644 (file)
@@ -317,3 +317,25 @@ void replace_index_entry_in_base(struct index_state *istate,
                istate->split_index->base->cache[new->index - 1] = new;
        }
 }
+
+void add_split_index(struct index_state *istate)
+{
+       if (!istate->split_index) {
+               init_split_index(istate);
+               istate->cache_changed |= SPLIT_INDEX_ORDERED;
+       }
+}
+
+void remove_split_index(struct index_state *istate)
+{
+       if (istate->split_index) {
+               /*
+                * can't discard_split_index(&the_index); because that
+                * will destroy split_index->base->cache[], which may
+                * be shared with the_index.cache[]. So yeah we're
+                * leaking a bit here.
+                */
+               istate->split_index = NULL;
+               istate->cache_changed |= SOMETHING_CHANGED;
+       }
+}
index c1324f521a87c427c531464c5e6424fe38d7d35b..df91c1bda8117fe7d0f25a0aaedce79370801ce0 100644 (file)
@@ -31,5 +31,7 @@ void merge_base_index(struct index_state *istate);
 void prepare_to_write_split_index(struct index_state *istate);
 void finish_writing_split_index(struct index_state *istate);
 void discard_split_index(struct index_state *istate);
+void add_split_index(struct index_state *istate);
+void remove_split_index(struct index_state *istate);
 
 #endif
index cf8e4bf532a63cf5133dcdf011968e9ac8b24d39..80047b1bb7b826699deff3e8c2590a2a00a5c121 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -574,7 +574,26 @@ static inline void strbuf_complete_line(struct strbuf *sb)
        strbuf_complete(sb, '\n');
 }
 
-extern int strbuf_branchname(struct strbuf *sb, const char *name);
+/*
+ * Copy "name" to "sb", expanding any special @-marks as handled by
+ * interpret_branch_name(). The result is a non-qualified branch name
+ * (so "foo" or "origin/master" instead of "refs/heads/foo" or
+ * "refs/remotes/origin/master").
+ *
+ * Note that the resulting name may not be a syntactically valid refname.
+ *
+ * If "allowed" is non-zero, restrict the set of allowed expansions. See
+ * interpret_branch_name() for details.
+ */
+extern void strbuf_branchname(struct strbuf *sb, const char *name,
+                             unsigned allowed);
+
+/*
+ * Like strbuf_branchname() above, but confirm that the result is
+ * syntactically valid to be used as a local branch name in refs/heads/.
+ *
+ * The return value is "0" if the result is valid, and "-1" otherwise.
+ */
 extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
 extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
index 93453909cf3225e2b4b9630d4013f76987ca8be8..bb069bc09734f473598d78493c1c08b1b4bd85e2 100644 (file)
@@ -333,7 +333,7 @@ static int parse_config(const char *var, const char *value, void *data)
                         strcmp(value, "all") &&
                         strcmp(value, "none"))
                        warning("Invalid parameter '%s' for config option "
-                                       "'submodule.%s.ignore'", value, var);
+                                       "'submodule.%s.ignore'", value, name.buf);
                else {
                        free((void *) submodule->ignore);
                        submodule->ignore = xstrdup(value);
index 0a2831d846d27e694ada4bc557a7d54a8bf2d8df..3200b7bb2b2360c7efab86b680ed5d0cafa5e9d3 100644 (file)
@@ -1514,3 +1514,85 @@ void absorb_git_dir_into_superproject(const char *prefix,
                strbuf_release(&sb);
        }
 }
+
+const char *get_superproject_working_tree(void)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf sb = STRBUF_INIT;
+       const char *one_up = real_path_if_valid("../");
+       const char *cwd = xgetcwd();
+       const char *ret = NULL;
+       const char *subpath;
+       int code;
+       ssize_t len;
+
+       if (!is_inside_work_tree())
+               /*
+                * FIXME:
+                * We might have a superproject, but it is harder
+                * to determine.
+                */
+               return NULL;
+
+       if (!one_up)
+               return NULL;
+
+       subpath = relative_path(cwd, one_up, &sb);
+
+       prepare_submodule_repo_env(&cp.env_array);
+       argv_array_pop(&cp.env_array);
+
+       argv_array_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
+                       "ls-files", "-z", "--stage", "--full-name", "--",
+                       subpath, NULL);
+       strbuf_reset(&sb);
+
+       cp.no_stdin = 1;
+       cp.no_stderr = 1;
+       cp.out = -1;
+       cp.git_cmd = 1;
+
+       if (start_command(&cp))
+               die(_("could not start ls-files in .."));
+
+       len = strbuf_read(&sb, cp.out, PATH_MAX);
+       close(cp.out);
+
+       if (starts_with(sb.buf, "160000")) {
+               int super_sub_len;
+               int cwd_len = strlen(cwd);
+               char *super_sub, *super_wt;
+
+               /*
+                * There is a superproject having this repo as a submodule.
+                * The format is <mode> SP <hash> SP <stage> TAB <full name> \0,
+                * We're only interested in the name after the tab.
+                */
+               super_sub = strchr(sb.buf, '\t') + 1;
+               super_sub_len = sb.buf + sb.len - super_sub - 1;
+
+               if (super_sub_len > cwd_len ||
+                   strcmp(&cwd[cwd_len - super_sub_len], super_sub))
+                       die (_("BUG: returned path string doesn't match cwd?"));
+
+               super_wt = xstrdup(cwd);
+               super_wt[cwd_len - super_sub_len] = '\0';
+
+               ret = real_path(super_wt);
+               free(super_wt);
+       }
+       strbuf_release(&sb);
+
+       code = finish_command(&cp);
+
+       if (code == 128)
+               /* '../' is not a git repository */
+               return NULL;
+       if (code == 0 && len == 0)
+               /* There is an unrelated git repository at '../' */
+               return NULL;
+       if (code)
+               die(_("ls-tree returned unexpected return code %d"), code);
+
+       return ret;
+}
index 05ab674f069282b3d5b20ca69d9a1fe8f295879b..c8a0c9cb2971219b807f7fe931c4dc5355b978ea 100644 (file)
@@ -93,4 +93,12 @@ extern void prepare_submodule_repo_env(struct argv_array *out);
 extern void absorb_git_dir_into_superproject(const char *prefix,
                                             const char *path,
                                             unsigned flags);
+
+/*
+ * Return the absolute path of the working tree of the superproject, which this
+ * project is a submodule of. If this repository is not a submodule of
+ * another repository, return NULL.
+ */
+extern const char *get_superproject_working_tree(void);
+
 #endif
index d5dab5a94f1d881b8b103f6290ba91e4ec596ec6..006d2a8152dc499f3588b130470285703b60dc92 100644 (file)
@@ -110,7 +110,12 @@ perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || {
 }
 
 perl -MCGI -MCGI::Util -MCGI::Carp -e 0 >/dev/null 2>&1 || {
-       skip_all='skipping gitweb tests, CGI module unusable'
+       skip_all='skipping gitweb tests, CGI & CGI::Util & CGI::Carp modules not available'
+       test_done
+}
+
+perl -mTime::HiRes -e 0 >/dev/null 2>&1 || {
+       skip_all='skipping gitweb tests, Time::HiRes module not available'
        test_done
 }
 
index 83a4f2ab86999876ecfb78c7e6dd108eb09b04ae..8e3ed6a76cb97831e85152af8e9da21d18ed2fc1 100644 (file)
@@ -66,6 +66,16 @@ static int iterate_cb(const char *var, const char *value, void *data)
        return 0;
 }
 
+static int early_config_cb(const char *var, const char *value, void *vdata)
+{
+       const char *key = vdata;
+
+       if (!strcmp(key, var))
+               printf("%s\n", value);
+
+       return 0;
+}
+
 int cmd_main(int argc, const char **argv)
 {
        int i, val;
@@ -73,6 +83,11 @@ int cmd_main(int argc, const char **argv)
        const struct string_list *strptr;
        struct config_set cs;
 
+       if (argc == 3 && !strcmp(argv[1], "read_early_config")) {
+               read_early_config(early_config_cb, (void *)argv[2]);
+               return 0;
+       }
+
        setup_git_directory();
 
        git_configset_init(&cs);
diff --git a/t/interop/.gitignore b/t/interop/.gitignore
new file mode 100644 (file)
index 0000000..49c78d3
--- /dev/null
@@ -0,0 +1,4 @@
+/trash directory*/
+/test-results/
+/.prove/
+/build/
diff --git a/t/interop/Makefile b/t/interop/Makefile
new file mode 100644 (file)
index 0000000..31a4bbc
--- /dev/null
@@ -0,0 +1,16 @@
+-include ../../config.mak
+export GIT_TEST_OPTIONS
+
+SHELL_PATH ?= $(SHELL)
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+T = $(sort $(wildcard i[0-9][0-9][0-9][0-9]-*.sh))
+
+all: $(T)
+
+$(T):
+       @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+clean:
+       rm -rf build "trash directory".* test-results
+
+.PHONY: all clean $(T)
diff --git a/t/interop/README b/t/interop/README
new file mode 100644 (file)
index 0000000..72d42bd
--- /dev/null
@@ -0,0 +1,85 @@
+Git version interoperability tests
+==================================
+
+This directory has interoperability tests for git. Each script is
+similar to the normal test scripts found in t/, but with the added twist
+that two special versions of git, "git.a" and "git.b", are available in
+the PATH. Individual tests can then check the interaction between the
+two versions.
+
+When you add a feature that handles backwards compatibility between git
+versions, it's encouraged to add a test here to make sure it behaves as
+you expect.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make".  This runs all
+the tests against their default versions.
+
+You can run a single test like:
+
+    $ ./i0000-basic.sh
+    ok 1 - bare git is forbidden
+    ok 2 - git.a version (v1.6.6.3)
+    ok 3 - git.b version (v2.11.1)
+    # passed all 3 test(s)
+    1..3
+
+Each test contains default versions to run against. You may override
+these by setting `GIT_TEST_VERSION_A` and `GIT_TEST_VERSION_B` in the
+environment. Note that not all combinations will give sensible outcomes
+for all tests (e.g., a test checking for a specific old/new interaction
+may want something "old" enough" and something "new" enough; see
+individual tests for details).
+
+Version names should be resolvable as revisions in the current
+repository. They will be exported and built as needed using the
+config.mak files found at the root of your working tree.
+
+The exception is the special version "." which uses the currently-built
+contents of your working tree.
+
+You can set the following variables (in the environment or in your config.mak):
+
+    GIT_INTEROP_MAKE_OPTS
+       Options to pass to `make` when building a git version (e.g.,
+       `-j8`).
+
+You can also pass any command-line options taken by ordinary git tests (e.g.,
+"-v").
+
+
+Naming Tests
+------------
+
+The interop test files are named like:
+
+       iNNNN-short-description.sh
+
+where N is a decimal digit.  The same conventions for choosing NNNN as
+for normal tests apply.
+
+
+Writing Tests
+-------------
+
+An interop test script starts like a normal script, declaring a few
+variables and then including interop-lib.sh (which includes test-lib.sh).
+Besides test_description, you should also set the $VERSION_A and $VERSION_B
+variables to give the default versions to test against. See t0000-basic.sh for
+an example.
+
+You can then use test_expect_success as usual, with a few differences:
+
+  1. The special commands "git.a" and "git.b" correspond to the
+     two versions.
+
+  2. You cannot call a bare "git". This is to prevent accidents where
+     you meant "git.a" or "git.b".
+
+  3. The trash directory is _not_ a git repository by default. You
+     should create one with the appropriate version of git.
+
+At the end of the script, call test_done as usual.
diff --git a/t/interop/i0000-basic.sh b/t/interop/i0000-basic.sh
new file mode 100755 (executable)
index 0000000..903e919
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Note that this test only works on real version numbers,
+# as it depends on matching the output to "git version".
+VERSION_A=v1.6.6.3
+VERSION_B=v2.11.1
+
+test_description='sanity test interop library'
+. ./interop-lib.sh
+
+test_expect_success 'bare git is forbidden' '
+       test_must_fail git version
+'
+
+test_expect_success "git.a version ($VERSION_A)" '
+       echo git version ${VERSION_A#v} >expect &&
+       git.a version >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success "git.b version ($VERSION_B)" '
+       echo git version ${VERSION_B#v} >expect &&
+       git.b version >actual &&
+       test_cmp expect actual
+'
+
+test_done
diff --git a/t/interop/i5500-git-daemon.sh b/t/interop/i5500-git-daemon.sh
new file mode 100755 (executable)
index 0000000..1daf694
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+VERSION_A=.
+VERSION_B=v1.0.0
+
+: ${LIB_GIT_DAEMON_PORT:=5500}
+LIB_GIT_DAEMON_COMMAND='git.a daemon'
+
+test_description='clone and fetch by older client'
+. ./interop-lib.sh
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+
+start_git_daemon --export-all
+
+repo=$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo
+
+test_expect_success "create repo served by $VERSION_A" '
+       git.a init "$repo" &&
+       git.a -C "$repo" commit --allow-empty -m one
+'
+
+test_expect_success "clone with $VERSION_B" '
+       git.b clone "$GIT_DAEMON_URL/repo" child &&
+       echo one >expect &&
+       git.a -C child log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success "fetch with $VERSION_B" '
+       git.a -C "$repo" commit --allow-empty -m two &&
+       (
+               cd child &&
+               git.b fetch
+       ) &&
+       echo two >expect &&
+       git.a -C child log -1 --format=%s FETCH_HEAD >actual &&
+       test_cmp expect actual
+'
+
+stop_git_daemon
+test_done
diff --git a/t/interop/interop-lib.sh b/t/interop/interop-lib.sh
new file mode 100644 (file)
index 0000000..3e0a291
--- /dev/null
@@ -0,0 +1,92 @@
+# Interoperability testing framework. Each script should source
+# this after setting default $VERSION_A and $VERSION_B variables.
+
+. ../../GIT-BUILD-OPTIONS
+INTEROP_ROOT=$(pwd)
+BUILD_ROOT=$INTEROP_ROOT/build
+
+build_version () {
+       if test -z "$1"
+       then
+               echo >&2 "error: test script did not set default versions"
+               return 1
+       fi
+
+       if test "$1" = "."
+       then
+               git rev-parse --show-toplevel
+               return 0
+       fi
+
+       sha1=$(git rev-parse "$1^{tree}") || return 1
+       dir=$BUILD_ROOT/$sha1
+
+       if test -e "$dir/.built"
+       then
+               echo "$dir"
+               return 0
+       fi
+
+       echo >&2 "==> Building $1..."
+
+       mkdir -p "$dir" || return 1
+
+       (cd "$(git rev-parse --show-cdup)" && git archive --format=tar "$sha1") |
+       (cd "$dir" && tar x) ||
+       return 1
+
+       for config in config.mak config.mak.autogen config.status
+       do
+               if test -e "$INTEROP_ROOT/../../$config"
+               then
+                       cp "$INTEROP_ROOT/../../$config" "$dir/" || return 1
+               fi
+       done
+
+       (
+               cd "$dir" &&
+               make $GIT_INTEROP_MAKE_OPTS >&2 &&
+               touch .built
+       ) || return 1
+
+       echo "$dir"
+}
+
+# Old versions of git don't have bin-wrappers, so let's give a rough emulation.
+wrap_git () {
+       write_script "$1" <<-EOF
+       GIT_EXEC_PATH="$2"
+       export GIT_EXEC_PATH
+       PATH="$2:\$PATH"
+       export GIT_EXEC_PATH
+       exec git "\$@"
+       EOF
+}
+
+generate_wrappers () {
+       mkdir -p .bin &&
+       wrap_git .bin/git.a "$DIR_A" &&
+       wrap_git .bin/git.b "$DIR_B" &&
+       write_script .bin/git <<-\EOF &&
+       echo >&2 fatal: test tried to run generic git
+       exit 1
+       EOF
+       PATH=$(pwd)/.bin:$PATH
+}
+
+VERSION_A=${GIT_TEST_VERSION_A:-$VERSION_A}
+VERSION_B=${GIT_TEST_VERSION_B:-$VERSION_B}
+
+if ! DIR_A=$(build_version "$VERSION_A") ||
+   ! DIR_B=$(build_version "$VERSION_B")
+then
+       echo >&2 "fatal: unable to build git versions"
+       exit 1
+fi
+
+TEST_DIRECTORY=$INTEROP_ROOT/..
+TEST_OUTPUT_DIRECTORY=$INTEROP_ROOT
+TEST_NO_CREATE_REPO=t
+. "$TEST_DIRECTORY"/test-lib.sh
+
+generate_wrappers || die "unable to set up interop test environment"
index f9cbd4793194fbc38ec9a2bde0eafad748735044..987d40680b4d23f9053c623529eaafa88da04616 100644 (file)
@@ -46,7 +46,8 @@ start_git_daemon() {
 
        say >&3 "Starting git daemon ..."
        mkfifo git_daemon_output
-       git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
+       ${LIB_GIT_DAEMON_COMMAND:-git daemon} \
+               --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
                --reuseaddr --verbose \
                --base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
                "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
index 16359d51ae5a58ac629391b27d6b42acfc2ddb5e..ebf172401b0ff9c280ad14991323bd8aaf85b5e0 100755 (executable)
@@ -15,7 +15,8 @@ test_perf 'rev-list --all --objects' '
 '
 
 test_expect_success 'create new unreferenced commit' '
-       commit=$(git commit-tree HEAD^{tree} -p HEAD)
+       commit=$(git commit-tree HEAD^{tree} -p HEAD) &&
+       test_export commit
 '
 
 test_perf 'rev-list $commit --not --all' '
index 15ee5d1d53391f38017cfd26bd23d94082a47365..b029586ccb2c8b3fd746a6c0bbdaca3966d42fa7 100755 (executable)
@@ -16,4 +16,9 @@ test_perf 'noop filter' '
        git filter-branch -f base..HEAD
 '
 
+test_perf 'noop prune-empty' '
+       git checkout --detach tip &&
+       git filter-branch -f --prune-empty base..HEAD
+'
+
 test_done
index 46f08ee0870ffd2b1e0eb7d159b159d25ef61ffb..ab4b8b06ae50c5e4ee7e128ded55dea7dddf7865 100644 (file)
@@ -83,7 +83,7 @@ test_perf_create_repo_from () {
        error "bug in the test script: not 2 parameters to test-create-repo"
        repo="$1"
        source="$2"
-       source_git="$(git -C "$source" rev-parse --git-dir)"
+       source_git="$("$MODERN_GIT" -C "$source" rev-parse --git-dir)"
        objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)"
        mkdir -p "$repo/.git"
        (
@@ -102,7 +102,7 @@ test_perf_create_repo_from () {
        ) &&
        (
                cd "$repo" &&
-               git init -q && {
+               "$MODERN_GIT" init -q && {
                        test_have_prereq SYMLINKS ||
                        git config core.symlinks false
                } &&
index e8adedadfdca8549b3c4f8f8a19d383a7611f4ad..c788d713ae927007866b5b73920de2674f816207 100755 (executable)
@@ -63,6 +63,9 @@ run_dirs_helper () {
                unset GIT_TEST_INSTALLED
        else
                GIT_TEST_INSTALLED="$mydir/bin-wrappers"
+               # Older versions of git lacked bin-wrappers; fallback to the
+               # files in the root.
+               test -d "$GIT_TEST_INSTALLED" || GIT_TEST_INSTALLED=$mydir
                export GIT_TEST_INSTALLED
        fi
        run_one_dir "$@"
index e0a69402324fed3c2fd7ca9495f5735742766108..58c0b7e9b6d99a6ea8a507da89eb66f3c8780c31 100755 (executable)
@@ -56,5 +56,13 @@ test_expect_success 'merge @{-100} before checking out that many branches yet' '
        test_must_fail git merge @{-100}
 '
 
+test_expect_success 'log -g @{-1}' '
+       git checkout -b last_branch &&
+       git checkout -b new_branch &&
+       echo "last_branch@{0}" >expect &&
+       git log -g --format=%gd @{-1} >actual &&
+       test_cmp expect actual
+'
+
 test_done
 
index 9ba2ba11c3cac88154dbb2d522b43adf98a8dee7..e8339393203c65fac29a647c5a0f827cf24a4cbb 100755 (executable)
@@ -102,7 +102,7 @@ test_expect_success 'config modification does not affect includes' '
 
 test_expect_success 'missing include files are ignored' '
        cat >.gitconfig <<-\EOF &&
-       [include]path = foo
+       [include]path = non-existent
        [test]value = yes
        EOF
        echo yes >expect &&
@@ -152,6 +152,62 @@ test_expect_success 'relative includes from stdin line fail' '
        test_must_fail git config --file - test.one
 '
 
+test_expect_success 'conditional include, both unanchored' '
+       git init foo &&
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:foo/\"]path=bar" >>.git/config &&
+               echo "[test]one=1" >.git/bar &&
+               echo 1 >expect &&
+               git config test.one >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'conditional include, $HOME expansion' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:~/foo/\"]path=bar2" >>.git/config &&
+               echo "[test]two=2" >.git/bar2 &&
+               echo 2 >expect &&
+               git config test.two >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'conditional include, full pattern' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:**/foo/**\"]path=bar3" >>.git/config &&
+               echo "[test]three=3" >.git/bar3 &&
+               echo 3 >expect &&
+               git config test.three >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'conditional include, relative path' '
+       echo "[includeIf \"gitdir:./foo/.git\"]path=bar4" >>.gitconfig &&
+       echo "[test]four=4" >bar4 &&
+       (
+               cd foo &&
+               echo 4 >expect &&
+               git config test.four >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'conditional include, both unanchored, icase' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir/i:FOO/\"]path=bar5" >>.git/config &&
+               echo "[test]five=5" >.git/bar5 &&
+               echo 5 >expect &&
+               git config test.five >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'include cycles are detected' '
        cat >.gitconfig <<-\EOF &&
        [test]value = gitconfig
diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh
new file mode 100755 (executable)
index 0000000..b97357b
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+test_description='Test read_early_config()'
+
+. ./test-lib.sh
+
+test_expect_success 'read early config' '
+       test_config early.config correct &&
+       test-config read_early_config early.config >output &&
+       test correct = "$(cat output)"
+'
+
+test_expect_success 'in a sub-directory' '
+       test_config early.config sub &&
+       mkdir -p sub &&
+       (
+               cd sub &&
+               test-config read_early_config early.config
+       ) >output &&
+       test sub = "$(cat output)"
+'
+
+test_expect_success 'ceiling' '
+       test_config early.config ceiling &&
+       mkdir -p sub &&
+       (
+               GIT_CEILING_DIRECTORIES="$PWD" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd sub &&
+               test-config read_early_config early.config
+       ) >output &&
+       test -z "$(cat output)"
+'
+
+test_expect_success 'ceiling #2' '
+       mkdir -p xdg/git &&
+       git config -f xdg/git/config early.config xdg &&
+       test_config early.config ceiling &&
+       mkdir -p sub &&
+       (
+               XDG_CONFIG_HOME="$PWD"/xdg &&
+               GIT_CEILING_DIRECTORIES="$PWD" &&
+               export GIT_CEILING_DIRECTORIES XDG_CONFIG_HOME &&
+               cd sub &&
+               test-config read_early_config early.config
+       ) >output &&
+       test xdg = "$(cat output)"
+'
+
+test_with_config () {
+       rm -rf throwaway &&
+       git init throwaway &&
+       (
+               cd throwaway &&
+               echo "$*" >.git/config &&
+               test-config read_early_config early.config
+       )
+}
+
+test_expect_success 'ignore .git/ with incompatible repository version' '
+       test_with_config "[core]repositoryformatversion = 999999" 2>err &&
+       grep "warning:.* Expected git repo version <= [1-9]" err
+'
+
+test_expect_failure 'ignore .git/ with invalid repository version' '
+       test_with_config "[core]repositoryformatversion = invalid"
+'
+
+
+test_expect_failure 'ignore .git/ with invalid config' '
+       test_with_config "["
+'
+
+test_done
index 9ed8b8ccba5db702eaf7b872bd4a1e1af1fd2192..03d3c7f6d65b46bf977adf76a6d68582206768c2 100755 (executable)
@@ -116,4 +116,18 @@ test_expect_success 'git-path inside sub-dir' '
        test_cmp expect actual
 '
 
+test_expect_success 'showing the superproject correctly' '
+       git rev-parse --show-superproject-working-tree >out &&
+       test_must_be_empty out &&
+
+       test_create_repo super &&
+       test_commit -C super test_commit &&
+       test_create_repo sub &&
+       test_commit -C sub test_commit &&
+       git -C super submodule add ../sub dir/sub &&
+       echo $(pwd)/super >expect  &&
+       git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
+       test_cmp expect out
+'
+
 test_done
index 6096f2c630947241a2dee410255aeebb818b14f9..af3ec0da5ac0f5e0f6e19616d9c55b611ba5a701 100755 (executable)
@@ -8,6 +8,7 @@ test_description='split index mode tests'
 sane_unset GIT_TEST_SPLIT_INDEX
 
 test_expect_success 'enable split index' '
+       git config splitIndex.maxPercentChange 100 &&
        git update-index --split-index &&
        test-dump-split-index .git/index >actual &&
        indexversion=$(test-index-version <.git/index) &&
@@ -19,12 +20,12 @@ test_expect_success 'enable split index' '
                own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339
                base=39d890139ee5356c7ef572216cebcd27aa41f9df
        fi &&
-       cat >expect <<EOF &&
-own $own
-base $base
-replacements:
-deletions:
-EOF
+       cat >expect <<-EOF &&
+       own $own
+       base $base
+       replacements:
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
@@ -32,51 +33,51 @@ test_expect_success 'add one file' '
        : >one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-base $base
-100644 $EMPTY_BLOB 0   one
-replacements:
-deletions:
-EOF
+       cat >expect <<-EOF &&
+       base $base
+       100644 $EMPTY_BLOB 0    one
+       replacements:
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'disable split index' '
        git update-index --no-split-index &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        BASE=$(test-dump-split-index .git/index | grep "^own" | sed "s/own/base/") &&
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-not a split index
-EOF
+       cat >expect <<-EOF &&
+       not a split index
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'enable split index again, "one" now belongs to base index"' '
        git update-index --split-index &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-$BASE
-replacements:
-deletions:
-EOF
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
@@ -84,18 +85,18 @@ test_expect_success 'modify original file, base index untouched' '
        echo modified >one &&
        git update-index one &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0       one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       q_to_tab >expect <<EOF &&
-$BASE
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
-replacements: 0
-deletions:
-EOF
+       q_to_tab >expect <<-EOF &&
+       $BASE
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+       replacements: 0
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
@@ -103,54 +104,54 @@ test_expect_success 'add another file, which stays index' '
        : >two &&
        git update-index --add two &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
-100644 $EMPTY_BLOB 0   two
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0       one
+       100644 $EMPTY_BLOB 0    two
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       q_to_tab >expect <<EOF &&
-$BASE
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
-100644 $EMPTY_BLOB 0   two
-replacements: 0
-deletions:
-EOF
+       q_to_tab >expect <<-EOF &&
+       $BASE
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+       100644 $EMPTY_BLOB 0    two
+       replacements: 0
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'remove file not in base index' '
        git update-index --force-remove two &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0       one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       q_to_tab >expect <<EOF &&
-$BASE
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
-replacements: 0
-deletions:
-EOF
+       q_to_tab >expect <<-EOF &&
+       $BASE
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+       replacements: 0
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'remove file in base index' '
        git update-index --force-remove one &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-EOF
+       cat >ls-files.expect <<-EOF &&
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-$BASE
-replacements:
-deletions: 0
-EOF
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions: 0
+       EOF
        test_cmp expect actual
 '
 
@@ -158,18 +159,18 @@ test_expect_success 'add original file back' '
        : >one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-$BASE
-100644 $EMPTY_BLOB 0   one
-replacements:
-deletions: 0
-EOF
+       cat >expect <<-EOF &&
+       $BASE
+       100644 $EMPTY_BLOB 0    one
+       replacements:
+       deletions: 0
+       EOF
        test_cmp expect actual
 '
 
@@ -177,26 +178,26 @@ test_expect_success 'add new file' '
        : >two &&
        git update-index --add two &&
        git ls-files --stage >actual &&
-       cat >expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-100644 $EMPTY_BLOB 0   two
-EOF
+       cat >expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       100644 $EMPTY_BLOB 0    two
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'unify index, two files remain' '
        git update-index --no-split-index &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-100644 $EMPTY_BLOB 0   two
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       100644 $EMPTY_BLOB 0    two
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-not a split index
-EOF
+       cat >expect <<-EOF &&
+       not a split index
+       EOF
        test_cmp expect actual
 '
 
@@ -216,4 +217,157 @@ test_expect_success 'rev-parse --shared-index-path' '
        )
 '
 
+test_expect_success 'set core.splitIndex config variable to true' '
+       git config core.splitIndex true &&
+       : >three &&
+       git update-index --add three &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<-EOF &&
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       one
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       three
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       two
+       EOF
+       test_cmp ls-files.expect ls-files.actual &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'set core.splitIndex config variable to false' '
+       git config core.splitIndex false &&
+       git update-index --force-remove three &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<-EOF &&
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       one
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       two
+       EOF
+       test_cmp ls-files.expect ls-files.actual &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       not a split index
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'set core.splitIndex config variable to true' '
+       git config core.splitIndex true &&
+       : >three &&
+       git update-index --add three &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual &&
+       : >four &&
+       git update-index --add four &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       four
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
+       git config --unset splitIndex.maxPercentChange &&
+       : >five &&
+       git update-index --add five &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual &&
+       : >six &&
+       git update-index --add six &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       six
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'check splitIndex.maxPercentChange set to 0' '
+       git config splitIndex.maxPercentChange 0 &&
+       : >seven &&
+       git update-index --add seven &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual &&
+       : >eight &&
+       git update-index --add eight &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'shared index files expire after 2 weeks by default' '
+       : >ten &&
+       git update-index --add ten &&
+       test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
+       just_under_2_weeks_ago=$((5-14*86400)) &&
+       test-chmtime =$just_under_2_weeks_ago .git/sharedindex.* &&
+       : >eleven &&
+       git update-index --add eleven &&
+       test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
+       just_over_2_weeks_ago=$((-1-14*86400)) &&
+       test-chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
+       : >twelve &&
+       git update-index --add twelve &&
+       test $(ls .git/sharedindex.* | wc -l) -le 2
+'
+
+test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' '
+       git config splitIndex.sharedIndexExpire "16.days.ago" &&
+       test-chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
+       : >thirteen &&
+       git update-index --add thirteen &&
+       test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
+       just_over_16_days_ago=$((-1-16*86400)) &&
+       test-chmtime =$just_over_16_days_ago .git/sharedindex.* &&
+       : >fourteen &&
+       git update-index --add fourteen &&
+       test $(ls .git/sharedindex.* | wc -l) -le 2
+'
+
+test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"' '
+       git config splitIndex.sharedIndexExpire never &&
+       just_10_years_ago=$((-365*10*86400)) &&
+       test-chmtime =$just_10_years_ago .git/sharedindex.* &&
+       : >fifteen &&
+       git update-index --add fifteen &&
+       test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
+       git config splitIndex.sharedIndexExpire now &&
+       just_1_second_ago=-1 &&
+       test-chmtime =$just_1_second_ago .git/sharedindex.* &&
+       : >sixteen &&
+       git update-index --add sixteen &&
+       test $(ls .git/sharedindex.* | wc -l) -le 2
+'
+
 test_done
index e36ed3b4e18e22986233f72e016b491c801117db..9f353c0efce82e4492b0f3ce3acce1756827462e 100755 (executable)
@@ -213,6 +213,31 @@ test_expect_success 'git branch --list -d t should fail' '
        test_path_is_missing .git/refs/heads/t
 '
 
+test_expect_success 'git branch --list -v with --abbrev' '
+       test_when_finished "git branch -D t" &&
+       git branch t &&
+       git branch -v --list t >actual.default &&
+       git branch -v --list --abbrev t >actual.abbrev &&
+       test_cmp actual.default actual.abbrev &&
+
+       git branch -v --list --no-abbrev t >actual.noabbrev &&
+       git branch -v --list --abbrev=0 t >actual.0abbrev &&
+       test_cmp actual.noabbrev actual.0abbrev &&
+
+       git branch -v --list --abbrev=36 t >actual.36abbrev &&
+       # how many hexdigits are used?
+       read name objdefault rest <actual.abbrev &&
+       read name obj36 rest <actual.36abbrev &&
+       objfull=$(git rev-parse --verify t) &&
+
+       # are we really getting abbreviations?
+       test "$obj36" != "$objdefault" &&
+       expr "$obj36" : "$objdefault" >/dev/null &&
+       test "$objfull" != "$obj36" &&
+       expr "$objfull" : "$obj36" >/dev/null
+
+'
+
 test_expect_success 'git branch --column' '
        COLUMNS=81 git branch --column=column >actual &&
        cat >expected <<\EOF &&
diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh
new file mode 100755 (executable)
index 0000000..698d9cc
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+test_description='interpreting exotic branch name arguments
+
+Branch name arguments are usually names which are taken to be inside of
+refs/heads/, but we interpret some magic syntax like @{-1}, @{upstream}, etc.
+This script aims to check the behavior of those corner cases.
+'
+. ./test-lib.sh
+
+expect_branch() {
+       git log -1 --format=%s "$1" >actual &&
+       echo "$2" >expect &&
+       test_cmp expect actual
+}
+
+expect_deleted() {
+       test_must_fail git rev-parse --verify "$1"
+}
+
+test_expect_success 'set up repo' '
+       test_commit one &&
+       test_commit two &&
+       git remote add origin foo.git
+'
+
+test_expect_success 'update branch via @{-1}' '
+       git branch previous one &&
+
+       git checkout previous &&
+       git checkout master &&
+
+       git branch -f @{-1} two &&
+       expect_branch previous two
+'
+
+test_expect_success 'update branch via local @{upstream}' '
+       git branch local one &&
+       git branch --set-upstream-to=local &&
+
+       git branch -f @{upstream} two &&
+       expect_branch local two
+'
+
+test_expect_success 'disallow updating branch via remote @{upstream}' '
+       git update-ref refs/remotes/origin/remote one &&
+       git branch --set-upstream-to=origin/remote &&
+
+       test_must_fail git branch -f @{upstream} two
+'
+
+test_expect_success 'create branch with pseudo-qualified name' '
+       git branch refs/heads/qualified two &&
+       expect_branch refs/heads/refs/heads/qualified two
+'
+
+test_expect_success 'delete branch via @{-1}' '
+       git branch previous-del &&
+
+       git checkout previous-del &&
+       git checkout master &&
+
+       git branch -D @{-1} &&
+       expect_deleted previous-del
+'
+
+test_expect_success 'delete branch via local @{upstream}' '
+       git branch local-del &&
+       git branch --set-upstream-to=local-del &&
+
+       git branch -D @{upstream} &&
+       expect_deleted local-del
+'
+
+test_expect_success 'delete branch via remote @{upstream}' '
+       git update-ref refs/remotes/origin/remote-del two &&
+       git branch --set-upstream-to=origin/remote-del &&
+
+       git branch -r -D @{upstream} &&
+       expect_deleted origin/remote-del
+'
+
+# Note that we create two oddly named local branches here. We want to make
+# sure that we do not accidentally delete either of them, even if
+# shorten_unambiguous_ref() tweaks the name to avoid ambiguity.
+test_expect_success 'delete @{upstream} expansion matches -r option' '
+       git update-ref refs/remotes/origin/remote-del two &&
+       git branch --set-upstream-to=origin/remote-del &&
+       git update-ref refs/heads/origin/remote-del two &&
+       git update-ref refs/heads/remotes/origin/remote-del two &&
+
+       test_must_fail git branch -D @{upstream} &&
+       expect_branch refs/heads/origin/remote-del two &&
+       expect_branch refs/heads/remotes/origin/remote-del two
+'
+
+test_expect_success 'disallow deleting remote branch via @{-1}' '
+       git update-ref refs/remotes/origin/previous one &&
+
+       git checkout -b origin/previous two &&
+       git checkout master &&
+
+       test_must_fail git branch -r -D @{-1} &&
+       expect_branch refs/remotes/origin/previous one &&
+       expect_branch refs/heads/origin/previous two
+'
+
+# The thing we are testing here is that "@" is the real branch refs/heads/@,
+# and not refs/heads/HEAD. These tests should not imply that refs/heads/@ is a
+# sane thing, but it _is_ technically allowed for now. If we disallow it, these
+# can be switched to test_must_fail.
+test_expect_success 'create branch named "@"' '
+       git branch -f @ one &&
+       expect_branch refs/heads/@ one
+'
+
+test_expect_success 'delete branch named "@"' '
+       git update-ref refs/heads/@ two &&
+       git branch -D @ &&
+       expect_deleted refs/heads/@
+'
+
+test_expect_success 'checkout does not treat remote @{upstream} as a branch' '
+       git update-ref refs/remotes/origin/checkout one &&
+       git branch --set-upstream-to=origin/checkout &&
+       git update-ref refs/heads/origin/checkout two &&
+       git update-ref refs/heads/remotes/origin/checkout two &&
+
+       git checkout @{upstream} &&
+       expect_branch HEAD one
+'
+
+test_done
index e37547f41a21ebda150aad6797012c9df3742382..b1602718f85468d17faa1cc0d7ae8854ac2f5407 100755 (executable)
@@ -31,6 +31,15 @@ test_expect_success setup '
 
 '
 
+test_expect_success 'cherry-pick -m complains of bogus numbers' '
+       # expect 129 here to distinguish between cases where
+       # there was nothing to cherry-pick
+       test_expect_code 129 git cherry-pick -m &&
+       test_expect_code 129 git cherry-pick -m foo b &&
+       test_expect_code 129 git cherry-pick -m -1 b &&
+       test_expect_code 129 git cherry-pick -m 0 b
+'
+
 test_expect_success 'cherry-pick a non-merge with -m should fail' '
 
        git reset --hard &&
index aaa258daa376ab6d18d71a04a517840e1b55d094..f9528fa00c4ad382a99460fbf5b2c9b7a96ec34b 100755 (executable)
@@ -412,4 +412,47 @@ test_expect_success 'patch-mode via -i prompts for files' '
        test_cmp expect actual
 '
 
+test_expect_success 'add -p handles globs' '
+       git reset --hard &&
+
+       mkdir -p subdir &&
+       echo base >one.c &&
+       echo base >subdir/two.c &&
+       git add "*.c" &&
+       git commit -m base &&
+
+       echo change >one.c &&
+       echo change >subdir/two.c &&
+       git add -p "*.c" <<-\EOF &&
+       y
+       y
+       EOF
+
+       cat >expect <<-\EOF &&
+       one.c
+       subdir/two.c
+       EOF
+       git diff --cached --name-only >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add -p does not expand argument lists' '
+       git reset --hard &&
+
+       echo content >not-changed &&
+       git add not-changed &&
+       git commit -m "add not-changed file" &&
+
+       echo change >file &&
+       GIT_TRACE=$(pwd)/trace.out git add -p . <<-\EOF &&
+       y
+       EOF
+
+       # we know that "file" must be mentioned since we actually
+       # update it, but we want to be sure that our "." pathspec
+       # was not expanded into the argument list of any command.
+       # So look only for "not-changed".
+       ! grep not-changed trace.out
+'
+
 test_done
index 505e1b4a7f421d377aa3798e94c90c14249bce91..b5865b385d3a0ca175dc683c75b7f650958a0469 100755 (executable)
@@ -484,7 +484,7 @@ test_expect_success 'test lonely missing ref' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy
        ) >/dev/null 2>error-m &&
-       test_cmp expect-error error-m
+       test_i18ncmp expect-error error-m
 '
 
 test_expect_success 'test missing ref after existing' '
@@ -492,7 +492,7 @@ test_expect_success 'test missing ref after existing' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy
        ) >/dev/null 2>error-em &&
-       test_cmp expect-error error-em
+       test_i18ncmp expect-error error-em
 '
 
 test_expect_success 'test missing ref before existing' '
@@ -500,7 +500,7 @@ test_expect_success 'test missing ref before existing' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A
        ) >/dev/null 2>error-me &&
-       test_cmp expect-error error-me
+       test_i18ncmp expect-error error-me
 '
 
 test_expect_success 'test --all, --depth, and explicit head' '
index 0fc5a7c596b5d5e01ecb43f81d3d6eaafe611b7b..177897ea0b1e00cc4ec0f0e43510411ab19f1681 100755 (executable)
@@ -1098,7 +1098,8 @@ test_expect_success 'fetch exact SHA1' '
                test_must_fail git cat-file -t $the_commit &&
 
                # fetching the hidden object should fail by default
-               test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy &&
+               test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
+               test_i18ngrep "Server does not allow request for unadvertised object" err &&
                test_must_fail git rev-parse --verify refs/heads/copy &&
 
                # the server side can allow it to succeed
diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh
new file mode 100755 (executable)
index 0000000..77b8cef
--- /dev/null
@@ -0,0 +1,200 @@
+#!/bin/sh
+
+test_description='test labels in pathspecs'
+. ./test-lib.sh
+
+test_expect_success 'setup a tree' '
+       cat <<-\EOF >expect &&
+       fileA
+       fileAB
+       fileAC
+       fileB
+       fileBC
+       fileC
+       fileNoLabel
+       fileSetLabel
+       fileUnsetLabel
+       fileValue
+       fileWrongLabel
+       sub/fileA
+       sub/fileAB
+       sub/fileAC
+       sub/fileB
+       sub/fileBC
+       sub/fileC
+       sub/fileNoLabel
+       sub/fileSetLabel
+       sub/fileUnsetLabel
+       sub/fileValue
+       sub/fileWrongLabel
+       EOF
+       mkdir sub &&
+       while read path
+       do
+               : >$path &&
+               git add $path || return 1
+       done <expect &&
+       git commit -m "initial commit" &&
+       git ls-files >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'pathspec with no attr' '
+       test_must_fail git ls-files ":(attr:)"
+'
+
+test_expect_success 'pathspec with labels and non existent .gitattributes' '
+       git ls-files ":(attr:label)" >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'setup .gitattributes' '
+       cat <<-\EOF >.gitattributes &&
+       fileA labelA
+       fileB labelB
+       fileC labelC
+       fileAB labelA labelB
+       fileAC labelA labelC
+       fileBC labelB labelC
+       fileUnsetLabel -label
+       fileSetLabel label
+       fileValue label=foo
+       fileWrongLabel label☺
+       EOF
+       git add .gitattributes &&
+       git commit -m "add attributes"
+'
+
+test_expect_success 'check specific set attr' '
+       cat <<-\EOF >expect &&
+       fileSetLabel
+       sub/fileSetLabel
+       EOF
+       git ls-files ":(attr:label)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check specific unset attr' '
+       cat <<-\EOF >expect &&
+       fileUnsetLabel
+       sub/fileUnsetLabel
+       EOF
+       git ls-files ":(attr:-label)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check specific value attr' '
+       cat <<-\EOF >expect &&
+       fileValue
+       sub/fileValue
+       EOF
+       git ls-files ":(attr:label=foo)" >actual &&
+       test_cmp expect actual &&
+       git ls-files ":(attr:label=bar)" >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'check unspecified attr' '
+       cat <<-\EOF >expect &&
+       .gitattributes
+       fileA
+       fileAB
+       fileAC
+       fileB
+       fileBC
+       fileC
+       fileNoLabel
+       fileWrongLabel
+       sub/fileA
+       sub/fileAB
+       sub/fileAC
+       sub/fileB
+       sub/fileBC
+       sub/fileC
+       sub/fileNoLabel
+       sub/fileWrongLabel
+       EOF
+       git ls-files ":(attr:!label)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check multiple unspecified attr' '
+       cat <<-\EOF >expect &&
+       .gitattributes
+       fileC
+       fileNoLabel
+       fileWrongLabel
+       sub/fileC
+       sub/fileNoLabel
+       sub/fileWrongLabel
+       EOF
+       git ls-files ":(attr:!labelB !labelA !label)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check label with more labels but excluded path' '
+       cat <<-\EOF >expect &&
+       fileAB
+       fileB
+       fileBC
+       EOF
+       git ls-files ":(attr:labelB)" ":(exclude)sub/" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check label excluding other labels' '
+       cat <<-\EOF >expect &&
+       fileAB
+       fileB
+       fileBC
+       sub/fileAB
+       sub/fileB
+       EOF
+       git ls-files ":(attr:labelB)" ":(exclude,attr:labelC)sub/" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fail on multiple attr specifiers in one pathspec item' '
+       test_must_fail git ls-files . ":(attr:labelB,attr:labelC)" 2>actual &&
+       test_i18ngrep "Only one" actual
+'
+
+test_expect_success 'fail if attr magic is used places not implemented' '
+       # The main purpose of this test is to check that we actually fail
+       # when you attempt to use attr magic in commands that do not implement
+       # attr magic. This test does not advocate git-add to stay that way,
+       # though, but git-add is convenient as it has its own internal pathspec
+       # parsing.
+       test_must_fail git add ":(attr:labelB)" 2>actual &&
+       test_i18ngrep "unsupported magic" actual
+'
+
+test_expect_success 'abort on giving invalid label on the command line' '
+       test_must_fail git ls-files . ":(attr:☺)"
+'
+
+test_expect_success 'abort on asking for wrong magic' '
+       test_must_fail git ls-files . ":(attr:-label=foo)" &&
+       test_must_fail git ls-files . ":(attr:!label=foo)"
+'
+
+test_expect_success 'check attribute list' '
+       cat <<-EOF >>.gitattributes &&
+       * whitespace=indent,trail,space
+       EOF
+       git ls-files ":(attr:whitespace=indent\,trail\,space)" >actual &&
+       git ls-files >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'backslash cannot be the last character' '
+       test_must_fail git ls-files ":(attr:label=foo\\ labelA=bar)" 2>actual &&
+       test_i18ngrep "not allowed as last character in attr value" actual
+'
+
+test_expect_success 'backslash cannot be used as a value' '
+       test_must_fail git ls-files ":(attr:label=f\\\oo)" 2>actual &&
+       test_i18ngrep "for value matching" actual
+'
+
+test_done
index cb8fbd8e5e03a0ff5a176cc947419c357fd65d99..7cb60799be1a109e2210350137c8754a5bc4bb7d 100755 (executable)
@@ -313,6 +313,27 @@ test_expect_success 'Tag name filtering allows slashes in tag names' '
        git cat-file tag X/2 > actual &&
        test_cmp expect actual
 '
+test_expect_success 'setup --prune-empty comparisons' '
+       git checkout --orphan master-no-a &&
+       git rm -rf . &&
+       unset test_tick &&
+       test_tick &&
+       GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" &&
+       test_commit --notick B B.t B Bx &&
+       git checkout -b branch-no-a Bx &&
+       test_commit D D.t D Dx &&
+       mkdir dir &&
+       test_commit dir/D dir/D.t dir/D dir/Dx &&
+       test_commit E E.t E Ex &&
+       git checkout master-no-a &&
+       test_commit C C.t C Cx &&
+       git checkout branch-no-a &&
+       git merge Cx -m "Merge tag '\''C'\'' into branch" &&
+       git tag Fx &&
+       test_commit G G.t G Gx &&
+       test_commit H H.t H Hx &&
+       git checkout branch
+'
 
 test_expect_success 'Prune empty commits' '
        git rev-list HEAD > expect &&
@@ -341,6 +362,22 @@ test_expect_success 'prune empty works even without index/tree filters' '
        test_cmp expect actual
 '
 
+test_expect_success '--prune-empty is able to prune root commit' '
+       git rev-list branch-no-a >expect &&
+       git branch testing H &&
+       git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t" testing &&
+       git rev-list testing >actual &&
+       git branch -D testing &&
+       test_cmp expect actual
+'
+
+test_expect_success '--prune-empty is able to prune entire branch' '
+       git branch prune-entire B &&
+       git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire &&
+       test_path_is_missing .git/refs/heads/prune-entire &&
+       test_must_fail git reflog exists refs/heads/prune-entire
+'
+
 test_expect_success '--remap-to-ancestor with filename filters' '
        git checkout master &&
        git reset --hard A &&
index c8dc665f2fdd0f0452779e6f8576395f8b3bdf11..4f3794d415e95b0b65ca29b829ce278b45ea5dbd 100755 (executable)
@@ -360,27 +360,37 @@ test_pager_choices                       'git aliasedlog'
 test_default_pager        expect_success 'git -p aliasedlog'
 test_PAGER_overrides      expect_success 'git -p aliasedlog'
 test_core_pager_overrides expect_success 'git -p aliasedlog'
-test_core_pager_subdir    expect_failure 'git -p aliasedlog'
+test_core_pager_subdir    expect_success 'git -p aliasedlog'
 test_GIT_PAGER_overrides  expect_success 'git -p aliasedlog'
 
 test_default_pager        expect_success 'git -p true'
 test_PAGER_overrides      expect_success 'git -p true'
 test_core_pager_overrides expect_success 'git -p true'
-test_core_pager_subdir    expect_failure 'git -p true'
+test_core_pager_subdir    expect_success 'git -p true'
 test_GIT_PAGER_overrides  expect_success 'git -p true'
 
 test_default_pager        expect_success test_must_fail 'git -p request-pull'
 test_PAGER_overrides      expect_success test_must_fail 'git -p request-pull'
 test_core_pager_overrides expect_success test_must_fail 'git -p request-pull'
-test_core_pager_subdir    expect_failure test_must_fail 'git -p request-pull'
+test_core_pager_subdir    expect_success test_must_fail 'git -p request-pull'
 test_GIT_PAGER_overrides  expect_success test_must_fail 'git -p request-pull'
 
 test_default_pager        expect_success test_must_fail 'git -p'
 test_PAGER_overrides      expect_success test_must_fail 'git -p'
 test_local_config_ignored expect_failure test_must_fail 'git -p'
-test_no_local_config_subdir expect_success test_must_fail 'git -p'
 test_GIT_PAGER_overrides  expect_success test_must_fail 'git -p'
 
+test_expect_success TTY 'core.pager in repo config works and retains cwd' '
+       sane_unset GIT_PAGER &&
+       test_config core.pager "cat >cwd-retained" &&
+       (
+               cd sub &&
+               rm -f cwd-retained &&
+               test_terminal git -p rev-parse HEAD &&
+               test_path_is_file cwd-retained
+       )
+'
+
 test_doesnt_paginate      expect_failure test_must_fail 'git -p nonsense'
 
 test_pager_choices                       'git shortlog'
index 25241f40967ef1fb547771e64c69927de2f101ce..0e7f30db2d9116ec5d40b02612c97f765f3a4414 100755 (executable)
@@ -428,7 +428,7 @@ run_dir_diff_test 'difftool --dir-diff branch from subdirectory' '
                git difftool --dir-diff $symlinks --extcmd ls branch >output &&
                # "sub" must only exist in "right"
                # "file" and "file2" must be listed in both "left" and "right"
-               grep sub output > sub-output &&
+               grep sub output >sub-output &&
                test_line_count = 1 sub-output &&
                grep file"$" output >file-output &&
                test_line_count = 2 file-output &&
@@ -591,6 +591,7 @@ test_expect_success 'difftool --no-symlinks detects conflict ' '
 '
 
 test_expect_success 'difftool properly honors gitlink and core.worktree' '
+       test_when_finished rm -rf submod/ule &&
        git submodule add ./. submod/ule &&
        test_config -C submod/ule diff.tool checktrees &&
        test_config -C submod/ule difftool.checktrees.cmd '\''
@@ -600,11 +601,13 @@ test_expect_success 'difftool properly honors gitlink and core.worktree' '
                cd submod/ule &&
                echo good >expect &&
                git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
-               test_cmp expect actual
+               test_cmp expect actual &&
+               rm -f expect actual
        )
 '
 
 test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' '
+       test_when_finished git reset --hard &&
        git init dirlinks &&
        (
                cd dirlinks &&
@@ -623,4 +626,64 @@ test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' '
        )
 '
 
+test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
+       test_when_finished git reset --hard &&
+       touch b &&
+       ln -s b c &&
+       git add b c &&
+       test_tick &&
+       git commit -m initial &&
+       touch d &&
+       rm c &&
+       ln -s d c &&
+       cat >expect <<-EOF &&
+               b
+               c
+
+               c
+       EOF
+       git difftool --symlinks --dir-diff --extcmd ls >output &&
+       grep -v ^/ output >actual &&
+       test_cmp expect actual &&
+
+       git difftool --no-symlinks --dir-diff --extcmd ls >output &&
+       grep -v ^/ output >actual &&
+       test_cmp expect actual &&
+
+       # The left side contains symlink "c" that points to "b"
+       test_config difftool.cat.cmd "cat \$LOCAL/c" &&
+       printf "%s\n" b >expect &&
+
+       git difftool --symlinks --dir-diff --tool cat >actual &&
+       test_cmp expect actual &&
+
+       git difftool --symlinks --no-symlinks --dir-diff --tool cat >actual &&
+       test_cmp expect actual &&
+
+       # The right side contains symlink "c" that points to "d"
+       test_config difftool.cat.cmd "cat \$REMOTE/c" &&
+       printf "%s\n" d >expect &&
+
+       git difftool --symlinks --dir-diff --tool cat >actual &&
+       test_cmp expect actual &&
+
+       git difftool --no-symlinks --dir-diff --tool cat >actual &&
+       test_cmp expect actual &&
+
+       # Deleted symlinks
+       rm -f c &&
+       cat >expect <<-EOF &&
+               b
+               c
+
+       EOF
+       git difftool --symlinks --dir-diff --extcmd ls >output &&
+       grep -v ^/ output >actual &&
+       test_cmp expect actual &&
+
+       git difftool --no-symlinks --dir-diff --extcmd ls >output &&
+       grep -v ^/ output >actual &&
+       test_cmp expect actual
+'
+
 test_done
index bb879a527d94e7329dccef15525421917676f56b..1319415ba8ec04f2b9d34ec984d3f8afb61fb551 100755 (executable)
@@ -18,6 +18,11 @@ then
     test_done
 fi
 
+if ! test_have_prereq NOT_ROOT; then
+       skip_all='When cvs is compiled with CVS_BADROOT commits as root fail'
+       test_done
+fi
+
 CVSROOT=$PWD/tmpcvsroot
 CVSWORK=$PWD/cvswork
 GIT_DIR=$PWD/.git
index 4c384ff02333e1413cc67b1b4a6bed47ae3d00db..804ce3850ff3f7838035fd316e26ee301b525ff0 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='git cvsimport basic tests'
 . ./lib-cvs.sh
 
+if ! test_have_prereq NOT_ROOT; then
+       skip_all='When cvs is compiled with CVS_BADROOT commits as root fail'
+       test_done
+fi
+
 test_expect_success PERL 'setup cvsroot environment' '
        CVSROOT=$(pwd)/cvsroot &&
        export CVSROOT
index 5828e06afd670fc9c563da8bf233c34778872e9c..417ed7f19f5fcec5fdf9828749e1e40b207a44e7 100644 (file)
@@ -204,6 +204,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
 static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
+       int ret = 0;
        struct git_transport_data *data = transport->data;
        struct ref *refs;
        char *dest = xstrdup(transport->url);
@@ -241,19 +242,22 @@ static int fetch_refs_via_pack(struct transport *transport,
                          &transport->pack_lockfile);
        close(data->fd[0]);
        close(data->fd[1]);
-       if (finish_connect(data->conn)) {
-               free_refs(refs);
-               refs = NULL;
-       }
+       if (finish_connect(data->conn))
+               ret = -1;
        data->conn = NULL;
        data->got_remote_heads = 0;
        data->options.self_contained_and_connected =
                args.self_contained_and_connected;
 
+       if (refs == NULL)
+               ret = -1;
+       if (report_unmatched_refs(to_fetch, nr_heads))
+               ret = -1;
+
        free_refs(refs_tmp);
        free_refs(refs);
        free(dest);
-       return (refs ? 0 : -1);
+       return ret;
 }
 
 static int push_had_errors(struct ref *ref)
@@ -467,11 +471,11 @@ void transport_print_push_status(const char *dest, struct ref *refs,
 {
        struct ref *ref;
        int n = 0;
-       unsigned char head_sha1[20];
+       struct object_id head_oid;
        char *head;
        int summary_width = transport_summary_width(refs);
 
-       head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
+       head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL);
 
        if (verbose) {
                for (ref = refs; ref; ref = ref->next)
index d47012048f81c92a82758d87f8726cb919501104..308cf3779eb05b0953edd5aaeeae26f95104240f 100644 (file)
@@ -121,7 +121,7 @@ static void status_printf_more(struct wt_status *s, const char *color,
 
 void wt_status_prepare(struct wt_status *s)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
 
        memset(s, 0, sizeof(*s));
        memcpy(s->color_palette, default_wt_status_colors,
@@ -129,7 +129,7 @@ void wt_status_prepare(struct wt_status *s)
        s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
        s->use_color = -1;
        s->relative_paths = 1;
-       s->branch = resolve_refdup("HEAD", 0, sha1, NULL);
+       s->branch = resolve_refdup("HEAD", 0, oid.hash, NULL);
        s->reference = "HEAD";
        s->fp = stdout;
        s->index_file = get_index_file();
@@ -1115,16 +1115,16 @@ static void abbrev_sha1_in_line(struct strbuf *line)
 
        split = strbuf_split_max(line, ' ', 3);
        if (split[0] && split[1]) {
-               unsigned char sha1[20];
+               struct object_id oid;
 
                /*
                 * strbuf_split_max left a space. Trim it and re-add
                 * it after abbreviation.
                 */
                strbuf_trim(split[1]);
-               if (!get_sha1(split[1]->buf, sha1)) {
+               if (!get_oid(split[1]->buf, &oid)) {
                        strbuf_reset(split[1]);
-                       strbuf_add_unique_abbrev(split[1], sha1,
+                       strbuf_add_unique_abbrev(split[1], oid.hash,
                                                 DEFAULT_ABBREV);
                        strbuf_addch(split[1], ' ');
                        strbuf_reset(line);
@@ -1340,7 +1340,7 @@ static void show_bisect_in_progress(struct wt_status *s,
 static char *get_branch(const struct worktree *wt, const char *path)
 {
        struct strbuf sb = STRBUF_INIT;
-       unsigned char sha1[20];
+       struct object_id oid;
        const char *branch_name;
 
        if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
@@ -1354,9 +1354,9 @@ static char *get_branch(const struct worktree *wt, const char *path)
                strbuf_remove(&sb, 0, branch_name - sb.buf);
        else if (starts_with(sb.buf, "refs/"))
                ;
-       else if (!get_sha1_hex(sb.buf, sha1)) {
+       else if (!get_oid_hex(sb.buf, &oid)) {
                strbuf_reset(&sb);
-               strbuf_add_unique_abbrev(&sb, sha1, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&sb, oid.hash, DEFAULT_ABBREV);
        } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
                goto got_nothing;
        else                    /* bisect */
@@ -1370,10 +1370,10 @@ static char *get_branch(const struct worktree *wt, const char *path)
 
 struct grab_1st_switch_cbdata {
        struct strbuf buf;
-       unsigned char nsha1[20];
+       struct object_id noid;
 };
 
-static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
+static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
                           const char *email, unsigned long timestamp, int tz,
                           const char *message, void *cb_data)
 {
@@ -1387,13 +1387,13 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
                return 0;
        target += strlen(" to ");
        strbuf_reset(&cb->buf);
-       hashcpy(cb->nsha1, nsha1);
+       oidcpy(&cb->noid, noid);
        end = strchrnul(target, '\n');
        strbuf_add(&cb->buf, target, end - target);
        if (!strcmp(cb->buf.buf, "HEAD")) {
                /* HEAD is relative. Resolve it to the right reflog entry. */
                strbuf_reset(&cb->buf);
-               strbuf_add_unique_abbrev(&cb->buf, nsha1, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&cb->buf, noid->hash, DEFAULT_ABBREV);
        }
        return 1;
 }
@@ -1402,7 +1402,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
 {
        struct grab_1st_switch_cbdata cb;
        struct commit *commit;
-       unsigned char sha1[20];
+       struct object_id oid;
        char *ref = NULL;
 
        strbuf_init(&cb.buf, 0);
@@ -1411,22 +1411,22 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
                return;
        }
 
-       if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&
+       if (dwim_ref(cb.buf.buf, cb.buf.len, oid.hash, &ref) == 1 &&
            /* sha1 is a commit? match without further lookup */
-           (!hashcmp(cb.nsha1, sha1) ||
+           (!oidcmp(&cb.noid, &oid) ||
             /* perhaps sha1 is a tag, try to dereference to a commit */
-            ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
-             !hashcmp(cb.nsha1, commit->object.oid.hash)))) {
+            ((commit = lookup_commit_reference_gently(oid.hash, 1)) != NULL &&
+             !oidcmp(&cb.noid, &commit->object.oid)))) {
                const char *from = ref;
                if (!skip_prefix(from, "refs/tags/", &from))
                        skip_prefix(from, "refs/remotes/", &from);
                state->detached_from = xstrdup(from);
        } else
                state->detached_from =
-                       xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));
-       hashcpy(state->detached_sha1, cb.nsha1);
-       state->detached_at = !get_sha1("HEAD", sha1) &&
-                            !hashcmp(sha1, state->detached_sha1);
+                       xstrdup(find_unique_abbrev(cb.noid.hash, DEFAULT_ABBREV));
+       hashcpy(state->detached_sha1, cb.noid.hash);
+       state->detached_at = !get_oid("HEAD", &oid) &&
+                            !hashcmp(oid.hash, state->detached_sha1);
 
        free(ref);
        strbuf_release(&cb.buf);
@@ -1476,22 +1476,22 @@ void wt_status_get_state(struct wt_status_state *state,
                         int get_detached_from)
 {
        struct stat st;
-       unsigned char sha1[20];
+       struct object_id oid;
 
        if (!stat(git_path_merge_head(), &st)) {
                state->merge_in_progress = 1;
        } else if (wt_status_check_rebase(NULL, state)) {
                ;               /* all set */
        } else if (!stat(git_path_cherry_pick_head(), &st) &&
-                       !get_sha1("CHERRY_PICK_HEAD", sha1)) {
+                       !get_oid("CHERRY_PICK_HEAD", &oid)) {
                state->cherry_pick_in_progress = 1;
-               hashcpy(state->cherry_pick_head_sha1, sha1);
+               hashcpy(state->cherry_pick_head_sha1, oid.hash);
        }
        wt_status_check_bisect(NULL, state);
        if (!stat(git_path_revert_head(), &st) &&
-           !get_sha1("REVERT_HEAD", sha1)) {
+           !get_oid("REVERT_HEAD", &oid)) {
                state->revert_in_progress = 1;
-               hashcpy(state->revert_head_sha1, sha1);
+               hashcpy(state->revert_head_sha1, oid.hash);
        }
 
        if (get_detached_from)
@@ -1730,12 +1730,14 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
                return;
        branch_name = s->branch;
 
+#define LABEL(string) (s->no_gettext ? (string) : _(string))
+
        if (s->is_initial)
-               color_fprintf(s->fp, header_color, _("Initial commit on "));
+               color_fprintf(s->fp, header_color, LABEL(N_("Initial commit on ")));
 
        if (!strcmp(s->branch, "HEAD")) {
                color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s",
-                             _("HEAD (no branch)"));
+                             LABEL(N_("HEAD (no branch)")));
                goto conclude;
        }
 
@@ -1760,8 +1762,6 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
        if (!upstream_is_gone && !num_ours && !num_theirs)
                goto conclude;
 
-#define LABEL(string) (s->no_gettext ? (string) : _(string))
-
        color_fprintf(s->fp, header_color, " [");
        if (upstream_is_gone) {
                color_fprintf(s->fp, header_color, LABEL(N_("gone")));
@@ -1785,34 +1785,24 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
 
 static void wt_shortstatus_print(struct wt_status *s)
 {
-       int i;
+       struct string_list_item *it;
 
        if (s->show_branch)
                wt_shortstatus_print_tracking(s);
 
-       for (i = 0; i < s->change.nr; i++) {
-               struct wt_status_change_data *d;
-               struct string_list_item *it;
+       for_each_string_list_item(it, &s->change) {
+               struct wt_status_change_data *d = it->util;
 
-               it = &(s->change.items[i]);
-               d = it->util;
                if (d->stagemask)
                        wt_shortstatus_unmerged(it, s);
                else
                        wt_shortstatus_status(it, s);
        }
-       for (i = 0; i < s->untracked.nr; i++) {
-               struct string_list_item *it;
-
-               it = &(s->untracked.items[i]);
+       for_each_string_list_item(it, &s->untracked)
                wt_shortstatus_other(it, s, "??");
-       }
-       for (i = 0; i < s->ignored.nr; i++) {
-               struct string_list_item *it;
 
-               it = &(s->ignored.items[i]);
+       for_each_string_list_item(it, &s->ignored)
                wt_shortstatus_other(it, s, "!!");
-       }
 }
 
 static void wt_porcelain_print(struct wt_status *s)