Merge branch 'js/mingw-getcwd'
authorJunio C Hamano <gitster@pobox.com>
Tue, 30 Oct 2018 06:43:48 +0000 (15:43 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 Oct 2018 06:43:48 +0000 (15:43 +0900)
The way the Windows port figures out the current directory has been
improved.

* js/mingw-getcwd:
  mingw: fix getcwd when the parent directory cannot be queried
  mingw: ensure `getcwd()` reports the correct case

122 files changed:
.clang-format
.editorconfig [new file with mode: 0644]
.gitignore
Documentation/RelNotes/2.20.0.txt
Documentation/config.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-checkout.txt
Documentation/git-diff.txt
Documentation/git-gc.txt
Documentation/git-help.txt
Documentation/git-merge-base.txt
Documentation/git-p4.txt
Documentation/git-send-email.txt
Documentation/git-show-branch.txt
Documentation/git-tag.txt
Documentation/git-update-ref.txt
Documentation/git-upload-pack.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitmodules.txt
Documentation/gitsubmodules.txt
Documentation/gitweb.conf.txt
Documentation/howto/update-hook-example.txt
Documentation/rev-list-options.txt
Makefile
alias.h
apply.c
builtin/branch.c
builtin/fetch-pack.c
builtin/help.c
builtin/ls-remote.c
builtin/mktree.c
builtin/pack-objects.c
builtin/receive-pack.c
builtin/repack.c
builtin/rev-list.c
builtin/submodule--helper.c
cache.h
ci/run-build-and-tests.sh
command-list.txt
commit-reach.c
commit-reach.h
commit-slab-impl.h
compat/mingw.c
compat/mingw.h
config.mak.dev
contrib/coccinelle/object_id.cocci
contrib/coccinelle/preincr.cocci [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/coverage-diff.sh [new file with mode: 0755]
contrib/subtree/Makefile
contrib/subtree/git-subtree.sh
diff.c
fetch-negotiator.h
fsck.c
fuzz-pack-headers.c [new file with mode: 0644]
fuzz-pack-idx.c [new file with mode: 0644]
git-compat-util.h
git-p4.py
git-send-email.perl
git-submodule.sh
git.c
graph.c
ident.c
khash.h
list-objects-filter-options.c
list-objects-filter-options.h
list-objects-filter.c
list-objects-filter.h
list-objects.c
midx.c
midx.h
pack-bitmap-write.c
pack-objects.c
pack-objects.h
pack-revindex.c
packfile.c
packfile.h
range-diff.c
read-cache.c
ref-filter.c
refs/packed-backend.c
rerere.c
revision.c
revision.h
run-command.c
split-index.c
strbuf.h
t/helper/test-tool.c
t/helper/test-tool.h
t/t0061-run-command.sh
t/t0410-partial-clone.sh
t/t1700-split-index.sh
t/t1701-racy-split-index.sh [new file with mode: 0755]
t/t2000-checkout-cache-clash.sh [deleted file]
t/t2000-conflict-when-checking-files-out.sh [new file with mode: 0755]
t/t2001-checkout-cache-clash.sh [deleted file]
t/t3206-range-diff.sh
t/t4053-diff-no-index.sh
t/t4214-log-graph-octopus.sh [new file with mode: 0755]
t/t5317-pack-objects-filter-objects.sh
t/t5321-pack-large-objects.sh [new file with mode: 0755]
t/t5410-receive-pack-alternates.sh
t/t5516-fetch-push.sh
t/t5616-partial-clone.sh
t/t6112-rev-list-filters-objects.sh
t/t7005-editor.sh
t/t7500-commit-template-squash-signoff.sh [moved from t/t7500-commit.sh with 99% similarity]
t/t7501-commit-basic-functionality.sh [moved from t/t7501-commit.sh with 100% similarity]
t/t7502-commit-porcelain.sh [moved from t/t7502-commit.sh with 100% similarity]
t/t7509-commit-authorship.sh [moved from t/t7509-commit.sh with 98% similarity]
t/t9832-unshelve.sh
tag.c
transport.c
unpack-trees.c
upload-pack.c
vcs-svn/fast_export.h
vcs-svn/line_buffer.h
vcs-svn/sliding_window.h
vcs-svn/svndiff.h
vcs-svn/svndump.h

index 12a89f9..de1c8b5 100644 (file)
@@ -6,6 +6,8 @@
 
 # Use tabs whenever we need to fill whitespace that spans at least from one tab
 # stop to the next one.
+#
+# These settings are mirrored in .editorconfig.  Keep them in sync.
 UseTab: Always
 TabWidth: 8
 IndentWidth: 8
diff --git a/.editorconfig b/.editorconfig
new file mode 100644 (file)
index 0000000..42cdc4b
--- /dev/null
@@ -0,0 +1,16 @@
+[*]
+charset = utf-8
+insert_final_newline = true
+
+# The settings for C (*.c and *.h) files are mirrored in .clang-format.  Keep
+# them in sync.
+[*.{c,h,sh,perl,pl,pm}]
+indent_style = tab
+tab_width = 8
+
+[*.py]
+indent_style = space
+indent_size = 4
+
+[COMMIT_EDITMSG]
+max_line_length = 72
index 9d1363a..64b3377 100644 (file)
@@ -1,3 +1,6 @@
+/fuzz_corpora
+/fuzz-pack-headers
+/fuzz-pack-idx
 /GIT-BUILD-OPTIONS
 /GIT-CFLAGS
 /GIT-LDFLAGS
index ab63166..3b77b70 100644 (file)
@@ -17,6 +17,12 @@ Backward Compatibility Notes
  * "git help -a" now gives verbose output (same as "git help -av").
    Those who want the old output may say "git help --no-verbose -a"..
 
+ * "git cpn --help", when "cpn" is an alias to, say, "cherry-pick -n",
+   reported only the alias expansion of "cpn" in earlier versions of
+   Git.  It now runs "git cherry-pick --help" to show the manual page
+   of the command, while sending the alias expansion to the standard
+   error stream.
+
 
 Updates since v2.19
 -------------------
@@ -101,6 +107,22 @@ UI, Workflows & Features
    advertisement.  The alternate refs that are advertised are now
    configurable with a pair of configuration variables.
 
+ * "git cmd --help" when "cmd" is aliased used to only say "cmd is
+   aliased to ...".  Now it shows that to the standard error stream
+   and runs "git $cmd --help" where $cmd is the first word of the
+   alias expansion.
+
+ * The documentation of "git gc" has been updated to mention that it
+   is no longer limited to "pruning away crufts" but also updates
+   ancillary files like commit-graph as a part of repository
+   optimization.
+
+ * "git p4 unshelve" improvements.
+
+ * The logic to select the default user name and e-mail on Windows has
+   been improved.
+   (merge 501afcb8b0 js/mingw-default-ident later to maint).
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -196,6 +218,18 @@ Performance, Internal Implementation, Development Support etc.
    object exists, even for paths that are outside of the partial
    checkout area.  The code has been updated to avoid such a check.
 
+ * To help developers, an EditorConfig file that attempts to follow
+   the project convention has been added.
+   (merge b548d698a0 bc/editorconfig later to maint).
+
+ * The result of coverage test can be combined with "git blame" to
+   check the test coverage of code introduced recently with a new
+   'coverage-diff' tool (in contrib/).
+   (merge 783faedd65 ds/coverage-diff later to maint).
+
+ * An experiment to fuzz test a few areas, hopefully we can gain more
+   coverage to various areas.
+
 
 Fixes since v2.19
 -----------------
@@ -290,6 +324,15 @@ Fixes since v2.19
    no blobs are needed.
    (merge 4c7f9567ea jt/non-blob-lazy-fetch later to maint).
 
+ * The codepath to support the experimental split-index mode had
+   remaining "racily clean" issues fixed.
+   (merge 4c490f3d32 sg/split-index-racefix later to maint).
+
+ * "git log --graph" showing an octopus merge sometimes miscounted the
+   number of display columns it is consuming to show the merge and its
+   parent commits, which has been corrected.
+   (merge 04005834ed np/log-graph-octopus-fix later to maint).
+
  * Code cleanup, docfix, build fix, etc.
    (merge 96a7501aad ts/doc-build-manpage-xsl-quietly later to maint).
    (merge b9b07efdb2 tg/conflict-marker-size later to maint).
@@ -313,3 +356,8 @@ Fixes since v2.19
    (merge 6e8fc70fce rs/sequencer-oidset-insert-avoids-dups later to maint).
    (merge ad0b8f9575 mw/doc-typofixes later to maint).
    (merge d9f079ad1a jc/how-to-document-api later to maint).
+   (merge b1492bf315 ma/t7005-bash-workaround later to maint).
+   (merge ac1f98a0df du/rev-parse-is-plumbing later to maint).
+   (merge ca8ed443a5 mm/doc-no-dashed-git later to maint).
+   (merge ce366a8144 du/get-tar-commit-id-is-plumbing later to maint).
+   (merge 61018fe9e0 du/cherry-is-plumbing later to maint).
index 5528279..09e95e9 100644 (file)
@@ -620,7 +620,7 @@ core.alternateRefsCommand::
        When advertising tips of available history from an alternate, use the shell to
        execute the specified command instead of linkgit:git-for-each-ref[1]. The
        first argument is the absolute path of the alternate. Output must contain one
-       hex object id per line (i.e., the same as produce by `git for-each-ref
+       hex object id per line (i.e., the same as produced by `git for-each-ref
        --format='%(objectname)'`).
 +
 Note that you cannot generally put `git for-each-ref` directly into the config
index 0f9ef2f..e999251 100644 (file)
@@ -633,11 +633,11 @@ and so at step 3) we compute f(X).
 Let's take the following graph as an example:
 
 -------------
-           G-H-I-J
-          /       \
+            G-H-I-J
+           /       \
 A-B-C-D-E-F         O
-          \       /
-           K-L-M-N
+           \       /
+            K-L-M-N
 -------------
 
 If we compute the following non optimal function on it:
@@ -649,25 +649,25 @@ g(X) = min(number_of_ancestors(X), number_of_descendants(X))
 we get:
 
 -------------
-           4 3 2 1
-           G-H-I-J
+            4 3 2 1
+            G-H-I-J
 1 2 3 4 5 6/       \0
 A-B-C-D-E-F         O
-          \       /
-           K-L-M-N
-           4 3 2 1
+           \       /
+            K-L-M-N
+            4 3 2 1
 -------------
 
 but with the algorithm used by git bisect we get:
 
 -------------
-           7 7 6 5
-           G-H-I-J
+            7 7 6 5
+            G-H-I-J
 1 2 3 4 5 6/       \0
 A-B-C-D-E-F         O
-          \       /
-           K-L-M-N
-           7 7 6 5
+           \       /
+            K-L-M-N
+            7 7 6 5
 -------------
 
 So we chose G, H, K or L as the best bisection point, which is better
@@ -773,7 +773,7 @@ forked of the main branch at a commit named "D" like this:
 -------------
 A-B-C-D-E-F-G  <--main
        \
-       H-I-J  <--dev
+        H-I-J  <--dev
 -------------
 
 The commit "D" is called a "merge base" for branch "main" and "dev"
index 9db0292..801de2f 100644 (file)
@@ -311,9 +311,9 @@ branch refers to a specific commit. Let's look at a repo with three
 commits, one of them tagged, and with branch 'master' checked out:
 
 ------------
-          HEAD (refers to branch 'master')
-           |
-           v
+           HEAD (refers to branch 'master')
+            |
+            v
 a---b---c  branch 'master' (refers to commit 'c')
     ^
     |
@@ -329,9 +329,9 @@ to commit 'd':
 ------------
 $ edit; git add; git commit
 
-              HEAD (refers to branch 'master')
-               |
-               v
+               HEAD (refers to branch 'master')
+                |
+                v
 a---b---c---d  branch 'master' (refers to commit 'd')
     ^
     |
@@ -398,7 +398,7 @@ at what happens when we then checkout master:
 ------------
 $ git checkout master
 
-              HEAD (refers to branch 'master')
+               HEAD (refers to branch 'master')
       e---f     |
      /          v
 a---b---c---d  branch 'master' (refers to commit 'd')
index b180f1f..030f162 100644 (file)
@@ -72,10 +72,10 @@ two blob objects, or changes between two files on disk.
        This form is to view the changes on the branch containing
        and up to the second <commit>, starting at a common ancestor
        of both <commit>.  "git diff A\...B" is equivalent to
-       "git diff $(git-merge-base A B) B".  You can omit any one
+       "git diff $(git merge-base A B) B".  You can omit any one
        of <commit>, which has the same effect as using HEAD instead.
 
-Just in case if you are doing something exotic, it should be
+Just in case you are doing something exotic, it should be
 noted that all of the <commit> in the above description, except
 in the last two forms that use ".." notations, can be any
 <tree>.
index f5bc98c..c20ee6c 100644 (file)
@@ -17,7 +17,8 @@ Runs a number of housekeeping tasks within the current repository,
 such as compressing file revisions (to reduce disk space and increase
 performance), removing unreachable objects which may have been
 created from prior invocations of 'git add', packing refs, pruning
-reflog, rerere metadata or stale working trees.
+reflog, rerere metadata or stale working trees. May also update ancillary
+indexes such as the commit-graph.
 
 Users are encouraged to run this task on a regular basis within
 each repository to maintain good disk space utilization and good
index 206e3ae..aab5453 100644 (file)
@@ -29,6 +29,10 @@ guide is brought up. The 'man' program is used by default for this
 purpose, but this can be overridden by other options or configuration
 variables.
 
+If an alias is given, git shows the definition of the alias on
+standard output. To get the manual page for the aliased command, use
+`git COMMAND --help`.
+
 Note that `git --help ...` is identical to `git help ...` because the
 former is internally converted into the latter.
 
index 502e00e..9f07f4f 100644 (file)
@@ -154,13 +154,13 @@ topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
 
-                        o---B2
+                        o---B2
                        /
        ---o---o---B1--o---o---o---B (origin/master)
                \
-                B0
+                B0
                  \
-                  D0---D1---D (topic)
+                  D0---D1---D (topic)
 
 where `origin/master` used to point at commits B0, B1, B2 and now it
 points at B, and your `topic` branch was started on top of it back
index 41780a5..f0a0280 100644 (file)
@@ -174,21 +174,21 @@ $ git p4 submit --update-shelve 1234 --update-shelve 2345
 Unshelve
 ~~~~~~~~
 Unshelving will take a shelved P4 changelist, and produce the equivalent git commit
-in the branch refs/remotes/p4/unshelved/<changelist>.
+in the branch refs/remotes/p4-unshelved/<changelist>.
 
 The git commit is created relative to the current origin revision (HEAD by default).
-If the shelved changelist's parent revisions differ, git-p4 will refuse to unshelve;
-you need to be unshelving onto an equivalent tree.
+A parent commit is created based on the origin, and then the unshelve commit is
+created based on that.
 
 The origin revision can be changed with the "--origin" option.
 
-If the target branch in refs/remotes/p4/unshelved already exists, the old one will
+If the target branch in refs/remotes/p4-unshelved already exists, the old one will
 be renamed.
 
 ----
 $ git p4 sync
 $ git p4 unshelve 12345
-$ git show refs/remotes/p4/unshelved/12345
+$ git show p4-unshelved/12345
 <submit more changes via p4 to the same files>
 $ git p4 unshelve 12345
 <refuses to unshelve until git is in sync with p4 again>
index 465a4ec..f6010ac 100644 (file)
@@ -321,16 +321,19 @@ Automating
        auto-cc of:
 +
 --
-- 'author' will avoid including the patch author
-- 'self' will avoid including the sender
+- 'author' will avoid including the patch author.
+- 'self' will avoid including the sender.
 - 'cc' will avoid including anyone mentioned in Cc lines in the patch header
   except for self (use 'self' for that).
 - 'bodycc' will avoid including anyone mentioned in Cc lines in the
   patch body (commit message) except for self (use 'self' for that).
 - 'sob' will avoid including anyone mentioned in Signed-off-by lines except
-   for self (use 'self' for that).
+  for self (use 'self' for that).
+- 'misc-by' will avoid including anyone mentioned in Acked-by,
+  Reviewed-by, Tested-by and other "-by" lines in the patch body,
+  except Signed-off-by (use 'sob' for that).
 - 'cccmd' will avoid running the --cc-cmd.
-- 'body' is equivalent to 'sob' + 'bodycc'
+- 'body' is equivalent to 'sob' + 'bodycc' + 'misc-by'.
 - 'all' will suppress all auto cc values.
 --
 +
index 262db04..4a01371 100644 (file)
@@ -19,7 +19,7 @@ DESCRIPTION
 -----------
 
 Shows the commit ancestry graph starting from the commits named
-with <rev>s or <globs>s (or all refs under refs/heads
+with <rev>s or <glob>s (or all refs under refs/heads
 and/or refs/tags) semi-visually.
 
 It cannot show more than 29 branches and commits at a time.
index 92f9c12..f2d644e 100644 (file)
@@ -187,6 +187,12 @@ This option is only applicable when listing tags without annotation lines.
        `--create-reflog`, but currently does not negate the setting of
        `core.logAllRefUpdates`.
 
+--format=<format>::
+       A string that interpolates `%(fieldname)` from a tag ref being shown
+       and the object it points at.  The format is the same as
+       that of linkgit:git-for-each-ref[1].  When unspecified,
+       defaults to `%(refname:strip=2)`.
+
 <tagname>::
        The name of the tag to create, delete, or describe.
        The new tag name must pass all checks defined by
@@ -198,12 +204,6 @@ This option is only applicable when listing tags without annotation lines.
        The object that the new tag will refer to, usually a commit.
        Defaults to HEAD.
 
-<format>::
-       A string that interpolates `%(fieldname)` from a tag ref being shown
-       and the object it points at.  The format is the same as
-       that of linkgit:git-for-each-ref[1].  When unspecified,
-       defaults to `%(refname:strip=2)`.
-
 CONFIGURATION
 -------------
 By default, 'git tag' in sign-with-default mode (-s) will use your
index fda8516..9671423 100644 (file)
@@ -129,8 +129,8 @@ a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
 symbolic refs before creating the log name) describing the change
 in ref value.  Log lines are formatted as:
 
-    oldsha1 SP newsha1 SP committer LF
-+
+    oldsha1 SP newsha1 SP committer LF
+
 Where "oldsha1" is the 40 character hexadecimal value previously
 stored in <ref>, "newsha1" is the 40 character hexadecimal value of
 <newvalue> and "committer" is the committer's name, email address
@@ -138,8 +138,8 @@ and date in the standard Git committer ident format.
 
 Optionally with -m:
 
-    oldsha1 SP newsha1 SP committer TAB message LF
-+
+    oldsha1 SP newsha1 SP committer TAB message LF
+
 Where all fields are as described above and "message" is the
 value supplied to the -m option.
 
index 822ad59..998f52d 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git-upload-pack' [--[no-]strict] [--timeout=<n>] [--stateless-rpc]
                  [--advertise-refs] <directory>
+
 DESCRIPTION
 -----------
 Invoked by 'git fetch-pack', learns what
index e2ee9fc..7352043 100644 (file)
@@ -270,8 +270,8 @@ Porcelain Format
 The porcelain format has a line per attribute.  Attributes are listed with a
 label and value separated by a single space.  Boolean attributes (like 'bare'
 and 'detached') are listed as a label only, and are only present if and only
-if the value is true.  An empty line indicates the end of a worktree.  For
-example:
+if the value is true.  The first attribute of a worktree is always `worktree`,
+an empty line indicates the end of the record.  For example:
 
 ------------
 $ git worktree list --porcelain
index 2ac9b1c..00156d6 100644 (file)
@@ -402,11 +402,11 @@ Git so take care if using a foreign front-end.
        of Git object directories which can be used to search for Git
        objects. New objects will not be written to these directories.
 +
-       Entries that begin with `"` (double-quote) will be interpreted
-       as C-style quoted paths, removing leading and trailing
-       double-quotes and respecting backslash escapes. E.g., the value
-       `"path-with-\"-and-:-in-it":vanilla-path` has two paths:
-       `path-with-"-and-:-in-it` and `vanilla-path`.
+Entries that begin with `"` (double-quote) will be interpreted
+as C-style quoted paths, removing leading and trailing
+double-quotes and respecting backslash escapes. E.g., the value
+`"path-with-\"-and-:-in-it":vanilla-path` has two paths:
+`path-with-"-and-:-in-it` and `vanilla-path`.
 
 `GIT_DIR`::
        If the `GIT_DIR` environment variable is set then it
index 92010b0..b8392fc 100644 (file)
@@ -303,21 +303,21 @@ number of pitfalls:
   attribute. If you decide to use the `working-tree-encoding` attribute
   in your repository, then it is strongly recommended to ensure that all
   clients working with the repository support it.
-
-  For example, Microsoft Visual Studio resources files (`*.rc`) or
-  PowerShell script files (`*.ps1`) are sometimes encoded in UTF-16.
-  If you declare `*.ps1` as files as UTF-16 and you add `foo.ps1` with
-  a `working-tree-encoding` enabled Git client, then `foo.ps1` will be
-  stored as UTF-8 internally. A client without `working-tree-encoding`
-  support will checkout `foo.ps1` as UTF-8 encoded file. This will
-  typically cause trouble for the users of this file.
-
-  If a Git client, that does not support the `working-tree-encoding`
-  attribute, adds a new file `bar.ps1`, then `bar.ps1` will be
-  stored "as-is" internally (in this example probably as UTF-16).
-  A client with `working-tree-encoding` support will interpret the
-  internal contents as UTF-8 and try to convert it to UTF-16 on checkout.
-  That operation will fail and cause an error.
++
+For example, Microsoft Visual Studio resources files (`*.rc`) or
+PowerShell script files (`*.ps1`) are sometimes encoded in UTF-16.
+If you declare `*.ps1` as files as UTF-16 and you add `foo.ps1` with
+a `working-tree-encoding` enabled Git client, then `foo.ps1` will be
+stored as UTF-8 internally. A client without `working-tree-encoding`
+support will checkout `foo.ps1` as UTF-8 encoded file. This will
+typically cause trouble for the users of this file.
++
+If a Git client, that does not support the `working-tree-encoding`
+attribute, adds a new file `bar.ps1`, then `bar.ps1` will be
+stored "as-is" internally (in this example probably as UTF-16).
+A client with `working-tree-encoding` support will interpret the
+internal contents as UTF-8 and try to convert it to UTF-16 on checkout.
+That operation will fail and cause an error.
 
 - Reencoding content to non-UTF encodings can cause errors as the
   conversion might not be UTF-8 round trip safe. If you suspect your
index 4d63def..312b6f9 100644 (file)
@@ -67,7 +67,8 @@ submodule.<name>.fetchRecurseSubmodules::
 submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. The following values are supported:
-
++
+--
        all;; The submodule will never be considered modified (but will
            nonetheless show up in the output of status and commit when it has
            been staged).
@@ -84,12 +85,14 @@ submodule.<name>.ignore::
            differences, and modifications to tracked and untracked files are
            shown. This is the default option.
 
-       If this option is also present in the submodules entry in .git/config
-       of the superproject, the setting there will override the one found in
-       .gitmodules.
-       Both settings can be overridden on the command line by using the
-       "--ignore-submodule" option. The 'git submodule' commands are not
-       affected by this setting.
+If this option is also present in the submodules entry in .git/config
+of the superproject, the setting there will override the one found in
+.gitmodules.
+
+Both settings can be overridden on the command line by using the
+"--ignore-submodule" option. The 'git submodule' commands are not
+affected by this setting.
+--
 
 submodule.<name>.shallow::
        When set to true, a clone of this submodule will be performed as a
index 504c5f1..57999e9 100644 (file)
@@ -169,11 +169,15 @@ ACTIVE SUBMODULES
 
 A submodule is considered active,
 
-  (a) if `submodule.<name>.active` is set to `true`
-     or
-  (b) if the submodule's path matches the pathspec in `submodule.active`
-     or
-  (c) if `submodule.<name>.url` is set.
+  a. if `submodule.<name>.active` is set to `true`
++
+or
+
+  b. if the submodule's path matches the pathspec in `submodule.active`
++
+or
+
+  c. if `submodule.<name>.url` is set.
 
 and these are evaluated in this order.
 
index 9c8982e..c0a326e 100644 (file)
@@ -19,10 +19,12 @@ end of a line is ignored.  See *perlsyn*(1) for details.
 
 An example:
 
-    # gitweb configuration file for http://git.example.org
-    #
-    our $projectroot = "/srv/git"; # FHS recommendation
-    our $site_name = 'Example.org >> Repos';
+------------------------------------------------
+# gitweb configuration file for http://git.example.org
+#
+our $projectroot = "/srv/git"; # FHS recommendation
+our $site_name = 'Example.org >> Repos';
+------------------------------------------------
 
 
 The configuration file is used to override the default settings that
@@ -357,6 +359,7 @@ $home_link_str::
 +
 For example, the following setting produces a breadcrumb trail like
 "home / dev / projects / ..." where "projects" is the home link.
++
 ----------------------------------------------------------------------------
     our @extra_breadcrumbs = (
       [ 'home' => 'https://www.example.org/' ],
@@ -901,14 +904,16 @@ To enable blame, pickaxe search, and snapshot support (allowing "tar.gz" and
 "zip" snapshots), while allowing individual projects to turn them off, put
 the following in your GITWEB_CONFIG file:
 
-       $feature{'blame'}{'default'} = [1];
-       $feature{'blame'}{'override'} = 1;
+--------------------------------------------------------------------------------
+$feature{'blame'}{'default'} = [1];
+$feature{'blame'}{'override'} = 1;
 
-       $feature{'pickaxe'}{'default'} = [1];
-       $feature{'pickaxe'}{'override'} = 1;
+$feature{'pickaxe'}{'default'} = [1];
+$feature{'pickaxe'}{'override'} = 1;
 
-       $feature{'snapshot'}{'default'} = ['zip', 'tgz'];
-       $feature{'snapshot'}{'override'} = 1;
+$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
+$feature{'snapshot'}{'override'} = 1;
+--------------------------------------------------------------------------------
 
 If you allow overriding for the snapshot feature, you can specify which
 snapshot formats are globally disabled. You can also add any command-line
index a5193b1..89821ec 100644 (file)
@@ -80,7 +80,7 @@ case "$1" in
       info "The branch '$1' is new..."
     else
       # updating -- make sure it is a fast-forward
-      mb=$(git-merge-base "$2" "$3")
+      mb=$(git merge-base "$2" "$3")
       case "$mb,$2" in
         "$2,$mb") info "Update is fast-forward" ;;
        *)        noff=y; info "This is not a fast-forward update.";;
index 7b27363..5f16729 100644 (file)
@@ -731,6 +731,11 @@ the requested refs.
 +
 The form '--filter=sparse:path=<path>' similarly uses a sparse-checkout
 specification contained in <path>.
++
+The form '--filter=tree:<depth>' omits all blobs and trees whose depth
+from the root tree is >= <depth> (minimum depth if an object is located
+at multiple depths in the commits traversed). Currently, only <depth>=0
+is supported, which omits all blobs and trees.
 
 --no-filter::
        Turn off any previous `--filter=` argument.
index d18ab0f..b08d5ea 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -590,6 +590,8 @@ XDIFF_OBJS =
 VCSSVN_OBJS =
 GENERATED_H =
 EXTRA_CPPFLAGS =
+FUZZ_OBJS =
+FUZZ_PROGRAMS =
 LIB_OBJS =
 PROGRAM_OBJS =
 PROGRAMS =
@@ -682,6 +684,14 @@ SCRIPTS = $(SCRIPT_SH_INS) \
 
 ETAGS_TARGET = TAGS
 
+FUZZ_OBJS += fuzz-pack-headers.o
+FUZZ_OBJS += fuzz-pack-idx.o
+
+# Always build fuzz objects even if not testing, to prevent bit-rot.
+all:: $(FUZZ_OBJS)
+
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
+
 # Empty...
 EXTRA_PROGRAMS =
 
@@ -2253,6 +2263,7 @@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST
 OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
+       $(FUZZ_OBJS) \
        common-main.o \
        git.o
 ifndef NO_CURL
@@ -2951,6 +2962,7 @@ clean: profile-clean coverage-clean cocciclean
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
+       $(RM) $(FUZZ_PROGRAMS)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
        $(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*
@@ -3075,3 +3087,24 @@ cover_db: coverage-report
 cover_db_html: cover_db
        cover -report html -outputdir cover_db_html cover_db
 
+
+### Fuzz testing
+#
+# Building fuzz targets generally requires a special set of compiler flags that
+# are not necessarily appropriate for general builds, and that vary greatly
+# depending on the compiler version used.
+#
+# An example command to build against libFuzzer from LLVM 4.0.0:
+#
+# make CC=clang CXX=clang++ \
+#      CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
+#      LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
+#      fuzz-all
+#
+.PHONY: fuzz-all
+
+$(FUZZ_PROGRAMS): all
+       $(QUIET_LINK)$(CXX) $(CFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
+               $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
+
+fuzz-all: $(FUZZ_PROGRAMS)
diff --git a/alias.h b/alias.h
index 79933f2..aef4843 100644 (file)
--- a/alias.h
+++ b/alias.h
@@ -1,5 +1,5 @@
-#ifndef __ALIAS_H__
-#define __ALIAS_H__
+#ifndef ALIAS_H
+#define ALIAS_H
 
 struct string_list;
 
diff --git a/apply.c b/apply.c
index fdae1d4..073d5f0 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -223,8 +223,8 @@ struct patch {
        struct fragment *fragments;
        char *result;
        size_t resultsize;
-       char old_sha1_prefix[41];
-       char new_sha1_prefix[41];
+       char old_oid_prefix[GIT_MAX_HEXSZ + 1];
+       char new_oid_prefix[GIT_MAX_HEXSZ + 1];
        struct patch *next;
 
        /* three-way fallback result */
@@ -1093,13 +1093,14 @@ static int gitdiff_index(struct apply_state *state,
         */
        const char *ptr, *eol;
        int len;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        ptr = strchr(line, '.');
-       if (!ptr || ptr[1] != '.' || 40 < ptr - line)
+       if (!ptr || ptr[1] != '.' || hexsz < ptr - line)
                return 0;
        len = ptr - line;
-       memcpy(patch->old_sha1_prefix, line, len);
-       patch->old_sha1_prefix[len] = 0;
+       memcpy(patch->old_oid_prefix, line, len);
+       patch->old_oid_prefix[len] = 0;
 
        line = ptr + 2;
        ptr = strchr(line, ' ');
@@ -1109,10 +1110,10 @@ static int gitdiff_index(struct apply_state *state,
                ptr = eol;
        len = ptr - line;
 
-       if (40 < len)
+       if (hexsz < len)
                return 0;
-       memcpy(patch->new_sha1_prefix, line, len);
-       patch->new_sha1_prefix[len] = 0;
+       memcpy(patch->new_oid_prefix, line, len);
+       patch->new_oid_prefix[len] = 0;
        if (*ptr == ' ')
                return gitdiff_oldmode(state, ptr + 1, patch);
        return 0;
@@ -2206,7 +2207,7 @@ static void reverse_patches(struct patch *p)
                SWAP(p->new_mode, p->old_mode);
                SWAP(p->is_new, p->is_delete);
                SWAP(p->lines_added, p->lines_deleted);
-               SWAP(p->old_sha1_prefix, p->new_sha1_prefix);
+               SWAP(p->old_oid_prefix, p->new_oid_prefix);
 
                for (; frag; frag = frag->next) {
                        SWAP(frag->newpos, frag->oldpos);
@@ -3144,15 +3145,16 @@ static int apply_binary(struct apply_state *state,
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        struct object_id oid;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        /*
         * For safety, we require patch index line to contain
-        * full 40-byte textual SHA1 for old and new, at least for now.
+        * full hex textual object ID for old and new, at least for now.
         */
-       if (strlen(patch->old_sha1_prefix) != 40 ||
-           strlen(patch->new_sha1_prefix) != 40 ||
-           get_oid_hex(patch->old_sha1_prefix, &oid) ||
-           get_oid_hex(patch->new_sha1_prefix, &oid))
+       if (strlen(patch->old_oid_prefix) != hexsz ||
+           strlen(patch->new_oid_prefix) != hexsz ||
+           get_oid_hex(patch->old_oid_prefix, &oid) ||
+           get_oid_hex(patch->new_oid_prefix, &oid))
                return error(_("cannot apply binary patch to '%s' "
                               "without full index line"), name);
 
@@ -3162,7 +3164,7 @@ static int apply_binary(struct apply_state *state,
                 * applies to.
                 */
                hash_object_file(img->buf, img->len, blob_type, &oid);
-               if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
+               if (strcmp(oid_to_hex(&oid), patch->old_oid_prefix))
                        return error(_("the patch applies to '%s' (%s), "
                                       "which does not match the "
                                       "current contents."),
@@ -3175,7 +3177,7 @@ static int apply_binary(struct apply_state *state,
                                       "'%s' but it is not empty"), name);
        }
 
-       get_oid_hex(patch->new_sha1_prefix, &oid);
+       get_oid_hex(patch->new_oid_prefix, &oid);
        if (is_null_oid(&oid)) {
                clear_image(img);
                return 0; /* deletion patch */
@@ -3191,7 +3193,7 @@ static int apply_binary(struct apply_state *state,
                if (!result)
                        return error(_("the necessary postimage %s for "
                                       "'%s' cannot be read"),
-                                    patch->new_sha1_prefix, name);
+                                    patch->new_oid_prefix, name);
                clear_image(img);
                img->buf = result;
                img->len = size;
@@ -3207,9 +3209,9 @@ static int apply_binary(struct apply_state *state,
 
                /* verify that the result matches */
                hash_object_file(img->buf, img->len, blob_type, &oid);
-               if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
+               if (strcmp(oid_to_hex(&oid), patch->new_oid_prefix))
                        return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
-                               name, patch->new_sha1_prefix, oid_to_hex(&oid));
+                               name, patch->new_oid_prefix, oid_to_hex(&oid));
        }
 
        return 0;
@@ -3568,7 +3570,7 @@ static int try_threeway(struct apply_state *state,
        /* Preimage the patch was prepared for */
        if (patch->is_new)
                write_object_file("", 0, blob_type, &pre_oid);
-       else if (get_oid(patch->old_sha1_prefix, &pre_oid) ||
+       else if (get_oid(patch->old_oid_prefix, &pre_oid) ||
                 read_blob_object(&buf, &pre_oid, patch->old_mode))
                return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
 
@@ -4060,13 +4062,13 @@ static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid)
            starts_with(++preimage, heading) &&
            /* does it record full SHA-1? */
            !get_oid_hex(preimage + sizeof(heading) - 1, oid) &&
-           preimage[sizeof(heading) + GIT_SHA1_HEXSZ - 1] == '\n' &&
+           preimage[sizeof(heading) + the_hash_algo->hexsz - 1] == '\n' &&
            /* does the abbreviated name on the index line agree with it? */
-           starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
+           starts_with(preimage + sizeof(heading) - 1, p->old_oid_prefix))
                return 0; /* it all looks fine */
 
        /* we may have full object name on the index line */
-       return get_oid_hex(p->old_sha1_prefix, oid);
+       return get_oid_hex(p->old_oid_prefix, oid);
 }
 
 /* Build an index that contains just the files needed for a 3way merge */
@@ -4095,7 +4097,7 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list)
                        else
                                return error(_("sha1 information is lacking or "
                                               "useless for submodule %s"), name);
-               } else if (!get_oid_blob(patch->old_sha1_prefix, &oid)) {
+               } else if (!get_oid_blob(patch->old_oid_prefix, &oid)) {
                        ; /* ok */
                } else if (!patch->lines_added && !patch->lines_deleted) {
                        /* mode-only change: update the current */
index c396c41..0c55f7f 100644 (file)
@@ -716,8 +716,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
                return 0;
-       }
-       else if (edit_description) {
+       } else if (edit_description) {
                const char *branch_name;
                struct strbuf branch_ref = STRBUF_INIT;
 
@@ -809,11 +808,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                git_config_set_multivar(buf.buf, NULL, NULL, 1);
                strbuf_release(&buf);
        } else if (argc > 0 && argc <= 2) {
-               struct branch *branch = branch_get(argv[0]);
-
-               if (!branch)
-                       die(_("no such branch '%s'"), argv[0]);
-
                if (filter.kind != FILTER_REFS_BRANCHES)
                        die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
 
index 1a1bc63..63e69a5 100644 (file)
@@ -16,13 +16,14 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
 {
        struct ref *ref;
        struct object_id oid;
+       const char *p;
 
-       if (!get_oid_hex(name, &oid)) {
-               if (name[GIT_SHA1_HEXSZ] == ' ') {
-                       /* <sha1> <ref>, find refname */
-                       name += GIT_SHA1_HEXSZ + 1;
-               } else if (name[GIT_SHA1_HEXSZ] == '\0') {
-                       ; /* <sha1>, leave sha1 as name */
+       if (!parse_oid_hex(name, &oid, &p)) {
+               if (*p == ' ') {
+                       /* <oid> <ref>, find refname */
+                       name = p + 1;
+               } else if (*p == '\0') {
+                       ; /* <oid>, leave oid as name */
                } else {
                        /* <ref>, clear cruft from oid */
                        oidclr(&oid);
index d83dac2..7739a5c 100644 (file)
@@ -415,9 +415,37 @@ static const char *check_git_cmd(const char* cmd)
 
        alias = alias_lookup(cmd);
        if (alias) {
-               printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
-               free(alias);
-               exit(0);
+               const char **argv;
+               int count;
+
+               /*
+                * handle_builtin() in git.c rewrites "git cmd --help"
+                * to "git help --exclude-guides cmd", so we can use
+                * exclude_guides to distinguish "git cmd --help" from
+                * "git help cmd". In the latter case, or if cmd is an
+                * alias for a shell command, just print the alias
+                * definition.
+                */
+               if (!exclude_guides || alias[0] == '!') {
+                       printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
+                       free(alias);
+                       exit(0);
+               }
+               /*
+                * Otherwise, we pretend that the command was "git
+                * word0 --help". We use split_cmdline() to get the
+                * first word of the alias, to ensure that we use the
+                * same rules as when the alias is actually
+                * used. split_cmdline() modifies alias in-place.
+                */
+               fprintf_ln(stderr, _("'%s' is aliased to '%s'"), cmd, alias);
+               count = split_cmdline(alias, &argv);
+               if (count < 0)
+                       die(_("bad alias.%s string: %s"), cmd,
+                           split_cmdline_strerror(count));
+               free(argv);
+               UNLEAK(alias);
+               return alias;
        }
 
        if (exclude_guides)
index 1a25df7..6a0cdec 100644 (file)
@@ -151,6 +151,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        }
 
        UNLEAK(sorting);
-       UNLEAK(ref_array);
+       ref_array_clear(&ref_array);
        return status;
 }
index 2dc4ad6..94e82b8 100644 (file)
@@ -98,7 +98,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
 
        *ntr++ = 0; /* now at the beginning of SHA1 */
 
-       path = ntr + 41;  /* at the beginning of name */
+       path = (char *)p + 1;  /* at the beginning of name */
        if (!nul_term_line && path[0] == '"') {
                struct strbuf p_uq = STRBUF_INIT;
                if (unquote_c_style(&p_uq, path, NULL))
index b059b86..e50c6cd 100644 (file)
@@ -2399,7 +2399,6 @@ static void init_threaded_search(void)
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
-       pthread_mutex_init(&to_pack.lock, NULL);
        old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
 }
 
index 7f089be..33187bd 100644 (file)
@@ -1025,6 +1025,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        const char *ret;
        struct object_id *old_oid = &cmd->old_oid;
        struct object_id *new_oid = &cmd->new_oid;
+       int do_update_worktree = 0;
 
        /* only refs/... are allowed */
        if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@@ -1050,9 +1051,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
                case DENY_UPDATE_INSTEAD:
-                       ret = update_worktree(new_oid->hash);
-                       if (ret)
-                               return ret;
+                       /* pass -- let other checks intervene first */
+                       do_update_worktree = 1;
                        break;
                }
        }
@@ -1117,6 +1117,12 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                return "hook declined";
        }
 
+       if (do_update_worktree) {
+               ret = update_worktree(new_oid->hash);
+               if (ret)
+                       return ret;
+       }
+
        if (is_null_oid(new_oid)) {
                struct strbuf err = STRBUF_INIT;
                if (!parse_object(the_repository, old_oid)) {
index c6a7943..0223f28 100644 (file)
@@ -235,8 +235,8 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
        while (strbuf_getline_lf(&line, out) != EOF) {
                char *promisor_name;
                int fd;
-               if (line.len != 40)
-                       die("repack: Expecting 40 character sha1 lines only from pack-objects.");
+               if (line.len != the_hash_algo->hexsz)
+                       die("repack: Expecting full hex object ID lines only from pack-objects.");
                string_list_append(names, line.buf);
 
                /*
@@ -407,8 +407,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        out = xfdopen(cmd.out, "r");
        while (strbuf_getline_lf(&line, out) != EOF) {
-               if (line.len != 40)
-                       die("repack: Expecting 40 character sha1 lines only from pack-objects.");
+               if (line.len != the_hash_algo->hexsz)
+                       die("repack: Expecting full hex object ID lines only from pack-objects.");
                string_list_append(&names, line.buf);
        }
        fclose(out);
@@ -535,14 +535,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        reprepare_packed_git(the_repository);
 
        if (delete_redundant) {
+               const int hexsz = the_hash_algo->hexsz;
                int opts = 0;
                string_list_sort(&names);
                for_each_string_list_item(item, &existing_packs) {
                        char *sha1;
                        size_t len = strlen(item->string);
-                       if (len < 40)
+                       if (len < hexsz)
                                continue;
-                       sha1 = item->string + len - 40;
+                       sha1 = item->string + len - hexsz;
                        if (!string_list_has_string(&names, sha1))
                                remove_redundant_pack(packdir, item->string);
                }
index cc1b705..5064d08 100644 (file)
@@ -6,6 +6,7 @@
 #include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
+#include "object.h"
 #include "object-store.h"
 #include "pack.h"
 #include "pack-bitmap.h"
@@ -209,7 +210,8 @@ static inline void finish_object__ma(struct object *obj)
         */
        switch (arg_missing_action) {
        case MA_ERROR:
-               die("missing blob object '%s'", oid_to_hex(&obj->oid));
+               die("missing %s object '%s'",
+                   type_name(obj->type), oid_to_hex(&obj->oid));
                return;
 
        case MA_ALLOW_ANY:
@@ -222,8 +224,8 @@ static inline void finish_object__ma(struct object *obj)
        case MA_ALLOW_PROMISOR:
                if (is_promisor_object(&obj->oid))
                        return;
-               die("unexpected missing blob object '%s'",
-                   oid_to_hex(&obj->oid));
+               die("unexpected missing %s object '%s'",
+                   type_name(obj->type), oid_to_hex(&obj->oid));
                return;
 
        default:
@@ -235,7 +237,7 @@ static inline void finish_object__ma(struct object *obj)
 static int finish_object(struct object *obj, const char *name, void *cb_data)
 {
        struct rev_list_info *info = cb_data;
-       if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) {
+       if (!has_object_file(&obj->oid)) {
                finish_object__ma(obj);
                return 1;
        }
@@ -373,6 +375,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        repo_init_revisions(the_repository, &revs, prefix);
        revs.abbrev = DEFAULT_ABBREV;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
+       revs.do_not_die_on_missing_tree = 1;
 
        /*
         * Scan the argument list before invoking setup_revisions(), so that we
index 80474c3..8e1db55 100644 (file)
@@ -1457,7 +1457,6 @@ static void determine_submodule_update_strategy(struct repository *r,
        key = xstrfmt("submodule.%s.update", sub->name);
 
        if (update) {
-               trace_printf("parsing update");
                if (parse_submodule_update_strategy(update, out) < 0)
                        die(_("Invalid update mode '%s' for submodule path '%s'"),
                                update, path);
@@ -1466,7 +1465,6 @@ static void determine_submodule_update_strategy(struct repository *r,
                        die(_("Invalid update mode '%s' configured for submodule path '%s'"),
                                val, path);
        } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
-               trace_printf("loaded thing");
                out->type = sub->update_strategy.type;
                out->command = sub->update_strategy.command;
        } else
diff --git a/cache.h b/cache.h
index 59c8a93..f7fabdd 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -783,6 +783,8 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
 #define CE_MATCH_REFRESH               0x10
 /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 #define CE_MATCH_IGNORE_FSMONITOR 0X20
+extern int is_racy_timestamp(const struct index_state *istate,
+                            const struct cache_entry *ce);
 extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
index 2a5bff4..cda170d 100755 (executable)
@@ -15,6 +15,8 @@ then
        export GIT_TEST_FULL_IN_PACK_ARRAY=true
        export GIT_TEST_OE_SIZE=10
        export GIT_TEST_OE_DELTA_SIZE=5
+       export GIT_TEST_COMMIT_GRAPH=1
+       export GIT_TEST_MULTI_PACK_INDEX=1
        make --quiet test
 fi
 
index c36ea3c..3a9af10 100644 (file)
@@ -62,7 +62,7 @@ git-check-mailmap                       purehelpers
 git-checkout                            mainporcelain           history
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
-git-cherry                              ancillaryinterrogators          complete
+git-cherry                              plumbinginterrogators          complete
 git-cherry-pick                         mainporcelain
 git-citool                              mainporcelain
 git-clean                               mainporcelain
@@ -96,7 +96,7 @@ git-for-each-ref                        plumbinginterrogators
 git-format-patch                        mainporcelain
 git-fsck                                ancillaryinterrogators          complete
 git-gc                                  mainporcelain
-git-get-tar-commit-id                   ancillaryinterrogators
+git-get-tar-commit-id                   plumbinginterrogators
 git-grep                                mainporcelain           info
 git-gui                                 mainporcelain
 git-hash-object                         plumbingmanipulators
@@ -153,7 +153,7 @@ git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
-git-rev-parse                           ancillaryinterrogators
+git-rev-parse                           plumbinginterrogators
 git-rm                                  mainporcelain           worktree
 git-send-email                          foreignscminterface             complete
 git-send-pack                           synchingrepositories
index 9f79ce0..a9da65c 100644 (file)
@@ -529,8 +529,8 @@ int commit_contains(struct ref_filter *filter, struct commit *commit,
 
 static int compare_commits_by_gen(const void *_a, const void *_b)
 {
-       const struct commit *a = (const struct commit *)_a;
-       const struct commit *b = (const struct commit *)_b;
+       const struct commit *a = *(const struct commit * const *)_a;
+       const struct commit *b = *(const struct commit * const *)_b;
 
        if (a->generation < b->generation)
                return -1;
@@ -593,8 +593,10 @@ int can_all_from_reach_with_flag(struct object_array *from,
                while (stack) {
                        struct commit_list *parent;
 
-                       if (stack->item->object.flags & with_flag) {
+                       if (stack->item->object.flags & (with_flag | RESULT)) {
                                pop_commit(&stack);
+                               if (stack)
+                                       stack->item->object.flags |= RESULT;
                                continue;
                        }
 
index 7d313e2..122a23a 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __COMMIT_REACH_H__
-#define __COMMIT_REACH_H__
+#ifndef COMMIT_REACH_H
+#define COMMIT_REACH_H
 
 #include "commit-slab.h"
 
index ac1e6d4..5c0eb91 100644 (file)
@@ -1,10 +1,10 @@
 #ifndef COMMIT_SLAB_IMPL_H
 #define COMMIT_SLAB_IMPL_H
 
-#define MAYBE_UNUSED __attribute__((__unused__))
+#include "git-compat-util.h"
 
 #define implement_static_commit_slab(slabname, elemtype) \
-       implement_commit_slab(slabname, elemtype, static MAYBE_UNUSED)
+       implement_commit_slab(slabname, elemtype, MAYBE_UNUSED static)
 
 #define implement_shared_commit_slab(slabname, elemtype) \
        implement_commit_slab(slabname, elemtype, )
index 19addfa..6c3c4ca 100644 (file)
@@ -5,6 +5,7 @@
 #include "../strbuf.h"
 #include "../run-command.h"
 #include "../cache.h"
+#include "win32/lazyload.h"
 
 #define HCAST(type, handle) ((type)(intptr_t)handle)
 
@@ -1844,18 +1845,63 @@ int mingw_getpagesize(void)
        return si.dwAllocationGranularity;
 }
 
+/* See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724435.aspx */
+enum EXTENDED_NAME_FORMAT {
+       NameDisplay = 3,
+       NameUserPrincipal = 8
+};
+
+static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
+{
+       DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
+               enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
+       static wchar_t wbuffer[1024];
+       DWORD len;
+
+       if (!INIT_PROC_ADDR(GetUserNameExW))
+               return NULL;
+
+       len = ARRAY_SIZE(wbuffer);
+       if (GetUserNameExW(type, wbuffer, &len)) {
+               char *converted = xmalloc((len *= 3));
+               if (xwcstoutf(converted, wbuffer, len) >= 0)
+                       return converted;
+               free(converted);
+       }
+
+       return NULL;
+}
+
+char *mingw_query_user_email(void)
+{
+       return get_extended_user_info(NameUserPrincipal);
+}
+
 struct passwd *getpwuid(int uid)
 {
+       static unsigned initialized;
        static char user_name[100];
-       static struct passwd p;
+       static struct passwd *p;
+       DWORD len;
+
+       if (initialized)
+               return p;
 
-       DWORD len = sizeof(user_name);
-       if (!GetUserName(user_name, &len))
+       len = sizeof(user_name);
+       if (!GetUserName(user_name, &len)) {
+               initialized = 1;
                return NULL;
-       p.pw_name = user_name;
-       p.pw_gecos = "unknown";
-       p.pw_dir = NULL;
-       return &p;
+       }
+
+       p = xmalloc(sizeof(*p));
+       p->pw_name = user_name;
+       p->pw_gecos = get_extended_user_info(NameDisplay);
+       if (!p->pw_gecos)
+               p->pw_gecos = "unknown";
+       p->pw_dir = NULL;
+
+       initialized = 1;
+       return p;
 }
 
 static HANDLE timer_event;
index 571019d..f31dcff 100644 (file)
@@ -424,6 +424,8 @@ static inline void convert_slashes(char *path)
 int mingw_offset_1st_component(const char *path);
 #define offset_1st_component mingw_offset_1st_component
 #define PATH_SEP ';'
+extern char *mingw_query_user_email(void);
+#define query_user_email mingw_query_user_email
 #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
 #define PRIuMAX "I64u"
 #define PRId64 "I64d"
index 92d2681..bbeeff4 100644 (file)
@@ -34,7 +34,6 @@ ifeq ($(filter extra-all,$(DEVOPTS)),)
 CFLAGS += -Wno-empty-body
 CFLAGS += -Wno-missing-field-initializers
 CFLAGS += -Wno-sign-compare
-CFLAGS += -Wno-unused-function
 CFLAGS += -Wno-unused-parameter
 endif
 endif
index d8bdb48..6a7cf3e 100644 (file)
 @@
-expression E1;
+struct object_id OID;
 @@
-- is_null_sha1(E1.hash)
-+ is_null_oid(&E1)
+- is_null_sha1(OID.hash)
++ is_null_oid(&OID)
 
 @@
-expression E1;
+struct object_id *OIDPTR;
 @@
-- is_null_sha1(E1->hash)
-+ is_null_oid(E1)
+- is_null_sha1(OIDPTR->hash)
++ is_null_oid(OIDPTR)
 
 @@
-expression E1;
+struct object_id OID;
 @@
-- sha1_to_hex(E1.hash)
-+ oid_to_hex(&E1)
+- sha1_to_hex(OID.hash)
++ oid_to_hex(&OID)
 
 @@
 identifier f != oid_to_hex;
-expression E1;
+struct object_id *OIDPTR;
 @@
   f(...) {<...
-- sha1_to_hex(E1->hash)
-+ oid_to_hex(E1)
+- sha1_to_hex(OIDPTR->hash)
++ oid_to_hex(OIDPTR)
   ...>}
 
 @@
-expression E1, E2;
+expression E;
+struct object_id OID;
 @@
-- sha1_to_hex_r(E1, E2.hash)
-+ oid_to_hex_r(E1, &E2)
+- sha1_to_hex_r(E, OID.hash)
++ oid_to_hex_r(E, &OID)
 
 @@
 identifier f != oid_to_hex_r;
-expression E1, E2;
+expression E;
+struct object_id *OIDPTR;
 @@
    f(...) {<...
-- sha1_to_hex_r(E1, E2->hash)
-+ oid_to_hex_r(E1, E2)
+- sha1_to_hex_r(E, OIDPTR->hash)
++ oid_to_hex_r(E, OIDPTR)
   ...>}
 
 @@
-expression E1;
+struct object_id OID;
 @@
-- hashclr(E1.hash)
-+ oidclr(&E1)
+- hashclr(OID.hash)
++ oidclr(&OID)
 
 @@
 identifier f != oidclr;
-expression E1;
+struct object_id *OIDPTR;
 @@
   f(...) {<...
-- hashclr(E1->hash)
-+ oidclr(E1)
+- hashclr(OIDPTR->hash)
++ oidclr(OIDPTR)
   ...>}
 
 @@
-expression E1, E2;
+struct object_id OID1, OID2;
 @@
-- hashcmp(E1.hash, E2.hash)
-+ oidcmp(&E1, &E2)
+- hashcmp(OID1.hash, OID2.hash)
++ oidcmp(&OID1, &OID2)
 
 @@
 identifier f != oidcmp;
-expression E1, E2;
+struct object_id *OIDPTR1, OIDPTR2;
 @@
   f(...) {<...
-- hashcmp(E1->hash, E2->hash)
-+ oidcmp(E1, E2)
+- hashcmp(OIDPTR1->hash, OIDPTR2->hash)
++ oidcmp(OIDPTR1, OIDPTR2)
   ...>}
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR;
+struct object_id OID;
 @@
-- hashcmp(E1->hash, E2.hash)
-+ oidcmp(E1, &E2)
+- hashcmp(OIDPTR->hash, OID.hash)
++ oidcmp(OIDPTR, &OID)
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR;
+struct object_id OID;
 @@
-- hashcmp(E1.hash, E2->hash)
-+ oidcmp(&E1, E2)
+- hashcmp(OID.hash, OIDPTR->hash)
++ oidcmp(&OID, OIDPTR)
 
 @@
-expression E1, E2;
+struct object_id OID1, OID2;
 @@
-- hashcpy(E1.hash, E2.hash)
-+ oidcpy(&E1, &E2)
+- hashcpy(OID1.hash, OID2.hash)
++ oidcpy(&OID1, &OID2)
 
 @@
 identifier f != oidcpy;
-expression E1, E2;
+struct object_id *OIDPTR1;
+struct object_id *OIDPTR2;
 @@
   f(...) {<...
-- hashcpy(E1->hash, E2->hash)
-+ oidcpy(E1, E2)
+- hashcpy(OIDPTR1->hash, OIDPTR2->hash)
++ oidcpy(OIDPTR1, OIDPTR2)
   ...>}
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR;
+struct object_id OID;
 @@
-- hashcpy(E1->hash, E2.hash)
-+ oidcpy(E1, &E2)
+- hashcpy(OIDPTR->hash, OID.hash)
++ oidcpy(OIDPTR, &OID)
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR;
+struct object_id OID;
 @@
-- hashcpy(E1.hash, E2->hash)
-+ oidcpy(&E1, E2)
+- hashcpy(OID.hash, OIDPTR->hash)
++ oidcpy(&OID, OIDPTR)
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR1;
+struct object_id *OIDPTR2;
 @@
-- oidcmp(E1, E2) == 0
-+ oideq(E1, E2)
+- oidcmp(OIDPTR1, OIDPTR2) == 0
++ oideq(OIDPTR1, OIDPTR2)
 
 @@
 identifier f != hasheq;
@@ -125,10 +133,11 @@ expression E1, E2;
   ...>}
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR1;
+struct object_id *OIDPTR2;
 @@
-- oidcmp(E1, E2) != 0
-+ !oideq(E1, E2)
+- oidcmp(OIDPTR1, OIDPTR2) != 0
++ !oideq(OIDPTR1, OIDPTR2)
 
 @@
 identifier f != hasheq;
diff --git a/contrib/coccinelle/preincr.cocci b/contrib/coccinelle/preincr.cocci
new file mode 100644 (file)
index 0000000..7fe1e8d
--- /dev/null
@@ -0,0 +1,5 @@
+@ preincrement @
+identifier i;
+@@
+- ++i > 1
++ i++
index db7fd87..5f55607 100644 (file)
@@ -1341,17 +1341,6 @@ _git_checkout ()
        esac
 }
 
-_git_cherry ()
-{
-       case "$cur" in
-       --*)
-               __gitcomp_builtin cherry
-               return
-       esac
-
-       __git_complete_refs
-}
-
 __git_cherry_pick_inprogress_options="--continue --quit --abort"
 
 _git_cherry_pick ()
diff --git a/contrib/coverage-diff.sh b/contrib/coverage-diff.sh
new file mode 100755 (executable)
index 0000000..4ec419f
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+# Usage: Run 'contrib/coverage-diff.sh <version1> <version2>' from source-root
+# after running
+#
+#     make coverage-test
+#     make coverage-report
+#
+# while checked out at <version2>. This script combines the *.gcov files
+# generated by the 'make' commands above with 'git diff <version1> <version2>'
+# to report new lines that are not covered by the test suite.
+
+V1=$1
+V2=$2
+
+diff_lines () {
+       perl -e '
+               my $line_num;
+               while (<>) {
+                       # Hunk header?  Grab the beginning in postimage.
+                       if (/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/) {
+                               $line_num = $1;
+                               next;
+                       }
+
+                       # Have we seen a hunk?  Ignore "diff --git" etc.
+                       next unless defined $line_num;
+
+                       # Deleted line? Ignore.
+                       if (/^-/) {
+                               next;
+                       }
+
+                       # Show only the line number of added lines.
+                       if (/^\+/) {
+                               print "$line_num\n";
+                       }
+                       # Either common context or added line appear in
+                       # the postimage.  Count it.
+                       $line_num++;
+               }
+       '
+}
+
+files=$(git diff --name-only "$V1" "$V2" -- \*.c)
+
+# create empty file
+>coverage-data.txt
+
+for file in $files
+do
+       git diff "$V1" "$V2" -- "$file" |
+       diff_lines |
+       sort >new_lines.txt
+
+       if ! test -s new_lines.txt
+       then
+               continue
+       fi
+
+       hash_file=$(echo $file | sed "s/\//\#/")
+
+       if ! test -s "$hash_file.gcov"
+       then
+               continue
+       fi
+
+       sed -ne '/#####:/{
+                       s/    #####://
+                       s/:.*//
+                       s/ //g
+                       p
+               }' "$hash_file.gcov" |
+       sort >uncovered_lines.txt
+
+       comm -12 uncovered_lines.txt new_lines.txt |
+       sed -e 's/$/\)/' |
+       sed -e 's/^/ /' >uncovered_new_lines.txt
+
+       grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
+       echo $file >>coverage-data.txt &&
+       git blame -s "$V2" -- "$file" |
+       sed 's/\t//g' |
+       grep -f uncovered_new_lines.txt >>coverage-data.txt &&
+       echo >>coverage-data.txt
+
+       rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
+done
+
+cat coverage-data.txt
+
+echo "Commits introducing uncovered code:"
+
+commit_list=$(cat coverage-data.txt |
+       grep -E '^[0-9a-f]{7,} ' |
+       awk '{print $1;}' |
+       sort |
+       uniq)
+
+(
+       for commit in $commit_list
+       do
+               git log --no-decorate --pretty=format:'%an      %h: %s' -1 $commit
+               echo
+       done
+) | sort
+
+rm coverage-data.txt
index 5c6cc4a..6906aae 100644 (file)
@@ -59,6 +59,10 @@ $(GIT_SUBTREE): $(GIT_SUBTREE_SH)
 
 doc: $(GIT_SUBTREE_DOC) $(GIT_SUBTREE_HTML)
 
+man: $(GIT_SUBTREE_DOC)
+
+html: $(GIT_SUBTREE_HTML)
+
 install: $(GIT_SUBTREE)
        $(INSTALL) -d -m 755 $(DESTDIR)$(gitexecdir)
        $(INSTALL) -m 755 $(GIT_SUBTREE) $(DESTDIR)$(gitexecdir)
index d3f39a8..147201d 100755 (executable)
@@ -231,12 +231,14 @@ cache_miss () {
 }
 
 check_parents () {
-       missed=$(cache_miss "$@")
+       missed=$(cache_miss "$1")
+       local indent=$(($2 + 1))
        for miss in $missed
        do
                if ! test -r "$cachedir/notree/$miss"
                then
                        debug "  incorrect order: $miss"
+                       process_split_commit "$miss" "" "$indent"
                fi
        done
 }
@@ -340,7 +342,12 @@ find_existing_splits () {
        revs="$2"
        main=
        sub=
-       git log --grep="^git-subtree-dir: $dir/*\$" \
+       local grep_format="^git-subtree-dir: $dir/*\$"
+       if test -n "$ignore_joins"
+       then
+               grep_format="^Add '$dir/' from commit '"
+       fi
+       git log --grep="$grep_format" \
                --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
        while read a b junk
        do
@@ -534,6 +541,7 @@ copy_or_skip () {
        nonidentical=
        p=
        gotparents=
+       copycommit=
        for parent in $newparents
        do
                ptree=$(toptree_for_commit $parent) || exit $?
@@ -541,7 +549,24 @@ copy_or_skip () {
                if test "$ptree" = "$tree"
                then
                        # an identical parent could be used in place of this rev.
-                       identical="$parent"
+                       if test -n "$identical"
+                       then
+                               # if a previous identical parent was found, check whether
+                               # one is already an ancestor of the other
+                               mergebase=$(git merge-base $identical $parent)
+                               if test "$identical" = "$mergebase"
+                               then
+                                       # current identical commit is an ancestor of parent
+                                       identical="$parent"
+                               elif test "$parent" != "$mergebase"
+                               then
+                                       # no common history; commit must be copied
+                                       copycommit=1
+                               fi
+                       else
+                               # first identical parent detected
+                               identical="$parent"
+                       fi
                else
                        nonidentical="$parent"
                fi
@@ -564,7 +589,6 @@ copy_or_skip () {
                fi
        done
 
-       copycommit=
        if test -n "$identical" && test -n "$nonidentical"
        then
                extras=$(git rev-list --count $identical..$nonidentical)
@@ -598,6 +622,58 @@ ensure_valid_ref_format () {
                die "'$1' does not look like a ref"
 }
 
+process_split_commit () {
+       local rev="$1"
+       local parents="$2"
+       local indent=$3
+
+       if test $indent -eq 0
+       then
+               revcount=$(($revcount + 1))
+       else
+               # processing commit without normal parent information;
+               # fetch from repo
+               parents=$(git rev-parse "$rev^@")
+               extracount=$(($extracount + 1))
+       fi
+
+       progress "$revcount/$revmax ($createcount) [$extracount]"
+
+       debug "Processing commit: $rev"
+       exists=$(cache_get "$rev")
+       if test -n "$exists"
+       then
+               debug "  prior: $exists"
+               return
+       fi
+       createcount=$(($createcount + 1))
+       debug "  parents: $parents"
+       check_parents "$parents" "$indent"
+       newparents=$(cache_get $parents)
+       debug "  newparents: $newparents"
+
+       tree=$(subtree_for_commit "$rev" "$dir")
+       debug "  tree is: $tree"
+
+       # ugly.  is there no better way to tell if this is a subtree
+       # vs. a mainline commit?  Does it matter?
+       if test -z "$tree"
+       then
+               set_notree "$rev"
+               if test -n "$newparents"
+               then
+                       cache_set "$rev" "$rev"
+               fi
+               return
+       fi
+
+       newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
+       debug "  newrev is: $newrev"
+       cache_set "$rev" "$newrev"
+       cache_set latest_new "$newrev"
+       cache_set latest_old "$rev"
+}
+
 cmd_add () {
        if test -e "$dir"
        then
@@ -689,12 +765,7 @@ cmd_split () {
                done
        fi
 
-       if test -n "$ignore_joins"
-       then
-               unrevs=
-       else
-               unrevs="$(find_existing_splits "$dir" "$revs")"
-       fi
+       unrevs="$(find_existing_splits "$dir" "$revs")"
 
        # We can't restrict rev-list to only $dir here, because some of our
        # parents have the $dir contents the root, and those won't match.
@@ -703,45 +774,11 @@ cmd_split () {
        revmax=$(eval "$grl" | wc -l)
        revcount=0
        createcount=0
+       extracount=0
        eval "$grl" |
        while read rev parents
        do
-               revcount=$(($revcount + 1))
-               progress "$revcount/$revmax ($createcount)"
-               debug "Processing commit: $rev"
-               exists=$(cache_get "$rev")
-               if test -n "$exists"
-               then
-                       debug "  prior: $exists"
-                       continue
-               fi
-               createcount=$(($createcount + 1))
-               debug "  parents: $parents"
-               newparents=$(cache_get $parents)
-               debug "  newparents: $newparents"
-
-               tree=$(subtree_for_commit "$rev" "$dir")
-               debug "  tree is: $tree"
-
-               check_parents $parents
-
-               # ugly.  is there no better way to tell if this is a subtree
-               # vs. a mainline commit?  Does it matter?
-               if test -z "$tree"
-               then
-                       set_notree "$rev"
-                       if test -n "$newparents"
-                       then
-                               cache_set "$rev" "$rev"
-                       fi
-                       continue
-               fi
-
-               newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
-               debug "  newrev is: $newrev"
-               cache_set "$rev" "$newrev"
-               cache_set latest_new "$newrev"
-               cache_set latest_old "$rev"
+               process_split_commit "$rev" "$parents" 0
        done || exit $?
 
        latest_new=$(cache_get latest_new)
diff --git a/diff.c b/diff.c
index 96833c8..8647db3 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1217,10 +1217,11 @@ static void dim_moved_lines(struct diff_options *o)
 static void emit_line_ws_markup(struct diff_options *o,
                                const char *set_sign, const char *set,
                                const char *reset,
-                               char sign, const char *line, int len,
+                               int sign_index, const char *line, int len,
                                unsigned ws_rule, int blank_at_eof)
 {
        const char *ws = NULL;
+       int sign = o->output_indicators[sign_index];
 
        if (o->ws_error_highlight & ws_rule) {
                ws = diff_get_color_opt(o, DIFF_WHITESPACE);
@@ -1300,8 +1301,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
                                set = diff_get_color_opt(o, DIFF_FILE_OLD);
                }
                emit_line_ws_markup(o, set_sign, set, reset,
-                                   o->output_indicators[OUTPUT_INDICATOR_CONTEXT],
-                                   line, len,
+                                   OUTPUT_INDICATOR_CONTEXT, line, len,
                                    flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
                break;
        case DIFF_SYMBOL_PLUS:
@@ -1345,8 +1345,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
                        flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
                }
                emit_line_ws_markup(o, set_sign, set, reset,
-                                   o->output_indicators[OUTPUT_INDICATOR_NEW],
-                                   line, len,
+                                   OUTPUT_INDICATOR_NEW, line, len,
                                    flags & DIFF_SYMBOL_CONTENT_WS_MASK,
                                    flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
                break;
@@ -1390,8 +1389,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
                                set = diff_get_color_opt(o, DIFF_CONTEXT_DIM);
                }
                emit_line_ws_markup(o, set_sign, set, reset,
-                                   o->output_indicators[OUTPUT_INDICATOR_OLD],
-                                   line, len,
+                                   OUTPUT_INDICATOR_OLD, line, len,
                                    flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
                break;
        case DIFF_SYMBOL_WORDS_PORCELAIN:
@@ -4304,12 +4302,12 @@ static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *is
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
 {
        /* Strip the prefix but do not molest /dev/null and absolute paths */
-       if (*namep && **namep != '/') {
+       if (*namep && !is_absolute_path(*namep)) {
                *namep += prefix_length;
                if (**namep == '/')
                        ++*namep;
        }
-       if (*otherp && **otherp != '/') {
+       if (*otherp && !is_absolute_path(*otherp)) {
                *otherp += prefix_length;
                if (**otherp == '/')
                        ++*otherp;
index ddb44a2..9e3967c 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef FETCH_NEGOTIATOR
-#define FETCH_NEGOTIATOR
+#ifndef FETCH_NEGOTIATOR_H
+#define FETCH_NEGOTIATOR_H
 
 struct commit;
 
diff --git a/fsck.c b/fsck.c
index 38624d2..68502ce 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -479,7 +479,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
                if (name) {
                        struct object *obj = &parents->item->object;
 
-                       if (++counter > 1)
+                       if (counter++)
                                put_object_name(options, obj, "%s^%d",
                                        name, counter);
                        else if (generation > 0)
diff --git a/fuzz-pack-headers.c b/fuzz-pack-headers.c
new file mode 100644 (file)
index 0000000..99da1d0
--- /dev/null
@@ -0,0 +1,14 @@
+#include "packfile.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       enum object_type type;
+       unsigned long len;
+
+       unpack_object_header_buffer((const unsigned char *)data,
+                                   (unsigned long)size, &type, &len);
+
+       return 0;
+}
diff --git a/fuzz-pack-idx.c b/fuzz-pack-idx.c
new file mode 100644 (file)
index 0000000..0c3d777
--- /dev/null
@@ -0,0 +1,13 @@
+#include "object-store.h"
+#include "packfile.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       struct packed_git p;
+
+       load_idx("fuzz-input", GIT_SHA1_RAWSZ, (void *)data, size, &p);
+
+       return 0;
+}
index 48c9555..96a3f86 100644 (file)
@@ -382,6 +382,10 @@ static inline char *git_find_last_dir_sep(const char *path)
 #define find_last_dir_sep git_find_last_dir_sep
 #endif
 
+#ifndef query_user_email
+#define query_user_email() NULL
+#endif
+
 #if defined(__HP_cc) && (__HP_cc >= 61000)
 #define NORETURN __attribute__((noreturn))
 #define NORETURN_PTR
@@ -408,6 +412,8 @@ static inline char *git_find_last_dir_sep(const char *path)
 #define LAST_ARG_MUST_BE_NULL
 #endif
 
+#define MAYBE_UNUSED __attribute__((__unused__))
+
 #include "compat/bswap.h"
 
 #include "wildmatch.h"
index 7fab255..1998c3e 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1306,6 +1306,9 @@ class GitLFS(LargeFileSystem):
             return LargeFileSystem.processContent(self, git_mode, relPath, contents)
 
 class Command:
+    delete_actions = ( "delete", "move/delete", "purge" )
+    add_actions = ( "add", "move/add" )
+
     def __init__(self):
         self.usage = "usage: %prog [options]"
         self.needsGit = True
@@ -2524,7 +2527,6 @@ class View(object):
         return ""
 
 class P4Sync(Command, P4UserMap):
-    delete_actions = ( "delete", "move/delete", "purge" )
 
     def __init__(self):
         Command.__init__(self)
@@ -2612,20 +2614,7 @@ class P4Sync(Command, P4UserMap):
         if self.verbose:
             print("checkpoint finished: " + out)
 
-    def cmp_shelved(self, path, filerev, revision):
-        """ Determine if a path at revision #filerev is the same as the file
-            at revision @revision for a shelved changelist. If they don't match,
-            unshelving won't be safe (we will get other changes mixed in).
-
-            This is comparing the revision that the shelved changelist is *based* on, not
-            the shelved changelist itself.
-        """
-        ret = p4Cmd(["diff2", "{0}#{1}".format(path, filerev), "{0}@{1}".format(path, revision)])
-        if verbose:
-            print("p4 diff2 path %s filerev %s revision %s => %s" % (path, filerev, revision, ret))
-        return ret["status"] == "identical"
-
-    def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0, origin_revision = 0):
+    def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0):
         self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
                              for path in self.cloneExclude]
         files = []
@@ -2650,17 +2639,6 @@ class P4Sync(Command, P4UserMap):
             file["type"] = commit["type%s" % fnum]
             if shelved:
                 file["shelved_cl"] = int(shelved_cl)
-
-                # For shelved changelists, check that the revision of each file that the
-                # shelve was based on matches the revision that we are using for the
-                # starting point for git-fast-import (self.initialParent). Otherwise
-                # the resulting diff will contain deltas from multiple commits.
-
-                if file["action"] != "add" and \
-                    not self.cmp_shelved(path, file["rev"], origin_revision):
-                    sys.exit("change {0} not based on {1} for {2}, cannot unshelve".format(
-                        commit["change"], self.initialParent, path))
-
             files.append(file)
             fnum = fnum + 1
         return files
@@ -2775,7 +2753,10 @@ class P4Sync(Command, P4UserMap):
         relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
         relPath = self.encodeWithUTF8(relPath)
         if verbose:
-            size = int(self.stream_file['fileSize'])
+            if 'fileSize' in self.stream_file:
+                size = int(self.stream_file['fileSize'])
+            else:
+                size = 0 # deleted files don't get a fileSize apparently
             sys.stdout.write('\r%s --> %s (%i MB)\n' % (file['depotFile'], relPath, size/1024/1024))
             sys.stdout.flush()
 
@@ -3029,7 +3010,7 @@ class P4Sync(Command, P4UserMap):
             print('Ignoring file outside of prefix: {0}'.format(path))
         return hasPrefix
 
-    def commit(self, details, files, branch, parent = ""):
+    def commit(self, details, files, branch, parent = "", allow_empty=False):
         epoch = details["time"]
         author = details["user"]
         jobs = self.extractJobsFromCommit(details)
@@ -3043,7 +3024,10 @@ class P4Sync(Command, P4UserMap):
         files = [f for f in files
             if self.inClientSpec(f['path']) and self.hasBranchPrefix(f['path'])]
 
-        if not files and not gitConfigBool('git-p4.keepEmptyCommits'):
+        if gitConfigBool('git-p4.keepEmptyCommits'):
+            allow_empty = True
+
+        if not files and not allow_empty:
             print('Ignoring revision {0} as it would produce an empty commit.'
                 .format(details['change']))
             return
@@ -3384,10 +3368,10 @@ class P4Sync(Command, P4UserMap):
         else:
             return None
 
-    def importChanges(self, changes, shelved=False, origin_revision=0):
+    def importChanges(self, changes, origin_revision=0):
         cnt = 1
         for change in changes:
-            description = p4_describe(change, shelved)
+            description = p4_describe(change)
             self.updateOptionDict(description)
 
             if not self.silent:
@@ -3457,7 +3441,7 @@ class P4Sync(Command, P4UserMap):
                                 print("Parent of %s not found. Committing into head of %s" % (branch, parent))
                             self.commit(description, filesForCommit, branch, parent)
                 else:
-                    files = self.extractFilesFromCommit(description, shelved, change, origin_revision)
+                    files = self.extractFilesFromCommit(description)
                     self.commit(description, files, self.branch,
                                 self.initialParent)
                     # only needed once, to connect to the previous commit
@@ -3953,7 +3937,7 @@ class P4Unshelve(Command):
         ]
         self.verbose = False
         self.noCommit = False
-        self.destbranch = "refs/remotes/p4/unshelved"
+        self.destbranch = "refs/remotes/p4-unshelved"
 
     def renameBranch(self, branch_name):
         """ Rename the existing branch to branch_name.N
@@ -3985,6 +3969,32 @@ class P4Unshelve(Command):
 
         sys.exit("could not find git-p4 commits in {0}".format(self.origin))
 
+    def createShelveParent(self, change, branch_name, sync, origin):
+        """ Create a commit matching the parent of the shelved changelist 'change'
+        """
+        parent_description = p4_describe(change, shelved=True)
+        parent_description['desc'] = 'parent for shelved changelist {}\n'.format(change)
+        files = sync.extractFilesFromCommit(parent_description, shelved=False, shelved_cl=change)
+
+        parent_files = []
+        for f in files:
+            # if it was added in the shelved changelist, it won't exist in the parent
+            if f['action'] in self.add_actions:
+                continue
+
+            # if it was deleted in the shelved changelist it must not be deleted
+            # in the parent - we might even need to create it if the origin branch
+            # does not have it
+            if f['action'] in self.delete_actions:
+                f['action'] = 'add'
+
+            parent_files.append(f)
+
+        sync.commit(parent_description, parent_files, branch_name,
+                parent=origin, allow_empty=True)
+        print("created parent commit for {0} based on {1} in {2}".format(
+            change, self.origin, branch_name))
+
     def run(self, args):
         if len(args) != 1:
             return False
@@ -3994,9 +4004,8 @@ class P4Unshelve(Command):
 
         sync = P4Sync()
         changes = args
-        sync.initialParent = self.origin
 
-        # use the first change in the list to construct the branch to unshelve into
+        # only one change at a time
         change = changes[0]
 
         # if the target branch already exists, rename it
@@ -4009,14 +4018,21 @@ class P4Unshelve(Command):
         sync.suppress_meta_comment = True
 
         settings = self.findLastP4Revision(self.origin)
-        origin_revision = settings['change']
         sync.depotPaths = settings['depot-paths']
         sync.branchPrefixes = sync.depotPaths
 
         sync.openStreams()
         sync.loadUserMapFromCache()
         sync.silent = True
-        sync.importChanges(changes, shelved=True, origin_revision=origin_revision)
+
+        # create a commit for the parent of the shelved changelist
+        self.createShelveParent(change, branch_name, sync, self.origin)
+
+        # create the commit for the shelved changelist itself
+        description = p4_describe(change, True)
+        files = sync.extractFilesFromCommit(description, True, change)
+
+        sync.commit(description, files, branch_name, "")
         sync.closeStreams()
 
         print("unshelved changelist {0} into {1}".format(change, branch_name))
index 2be5dac..58c6aa9 100755 (executable)
@@ -94,7 +94,7 @@ git send-email --dump-aliases
     --identity              <str>  * Use the sendemail.<id> options.
     --to-cmd                <str>  * Email To: via `<str> \$patch_path`
     --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
-    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
+    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
     --[no-]cc-cover                * Email Cc: addresses in the cover letter.
     --[no-]to-cover                * Email To: addresses in the cover letter.
     --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
@@ -454,13 +454,13 @@ my(%suppress_cc);
 if (@suppress_cc) {
        foreach my $entry (@suppress_cc) {
                die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
-                       unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
+                       unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc|misc-by)$/;
                $suppress_cc{$entry} = 1;
        }
 }
 
 if ($suppress_cc{'all'}) {
-       foreach my $entry (qw (cccmd cc author self sob body bodycc)) {
+       foreach my $entry (qw (cccmd cc author self sob body bodycc misc-by)) {
                $suppress_cc{$entry} = 1;
        }
        delete $suppress_cc{'all'};
@@ -471,7 +471,7 @@ $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
 $suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
 
 if ($suppress_cc{'body'}) {
-       foreach my $entry (qw (sob bodycc)) {
+       foreach my $entry (qw (sob bodycc misc-by)) {
                $suppress_cc{$entry} = 1;
        }
        delete $suppress_cc{'body'};
@@ -1681,7 +1681,7 @@ sub process_file {
        # Now parse the message body
        while(<$fh>) {
                $message .=  $_;
-               if (/^(Signed-off-by|Cc): (.*)/i) {
+               if (/^([a-z-]*-by|Cc): (.*)/i) {
                        chomp;
                        my ($what, $c) = ($1, $2);
                        # strip garbage for the address we'll use:
@@ -1691,8 +1691,18 @@ sub process_file {
                        if ($sc eq $sender) {
                                next if ($suppress_cc{'self'});
                        } else {
-                               next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i;
-                               next if $suppress_cc{'bodycc'} and $what =~ /Cc/i;
+                               if ($what =~ /^Signed-off-by$/i) {
+                                       next if $suppress_cc{'sob'};
+                               } elsif ($what =~ /-by$/i) {
+                                       next if $suppress_cc{'misc-by'};
+                               } elsif ($what =~ /Cc/i) {
+                                       next if $suppress_cc{'bodycc'};
+                               }
+                       }
+                       if ($c !~ /.+@.+|<.+>/) {
+                               printf("(body) Ignoring %s from line '%s'\n",
+                                       $what, $_) unless $quiet;
+                               next;
                        }
                        push @cc, $c;
                        printf(__("(body) Adding cc: %s from line '%s'\n"),
index 1b568e2..c09eb3e 100755 (executable)
@@ -82,6 +82,11 @@ isnumber()
        n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
 }
 
+# Given a full hex object ID, is this the zero OID?
+is_zero_oid () {
+       echo "$1" | sane_egrep '^0+$' >/dev/null 2>&1
+}
+
 # Sanitize the local git environment for use within a submodule. We
 # can't simply use clear_local_git_env since we want to preserve some
 # of the settings from GIT_CONFIG_PARAMETERS.
@@ -780,7 +785,7 @@ cmd_summary() {
        while read -r mod_src mod_dst sha1_src sha1_dst status name
        do
                if test -z "$cached" &&
-                       test $sha1_dst = 0000000000000000000000000000000000000000
+                       is_zero_oid $sha1_dst
                then
                        case "$mod_dst" in
                        160000)
diff --git a/git.c b/git.c
index 5920f80..adac132 100644 (file)
--- a/git.c
+++ b/git.c
@@ -318,6 +318,9 @@ static int handle_alias(int *argcp, const char ***argv)
        alias_command = (*argv)[0];
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
+               if (*argcp > 1 && !strcmp((*argv)[1], "-h"))
+                       fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
+                                  alias_command, alias_string);
                if (alias_string[0] == '!') {
                        struct child_process child = CHILD_PROCESS_INIT;
                        int nongit_ok;
diff --git a/graph.c b/graph.c
index e1f6d3b..f531354 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -842,27 +842,55 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
 }
 
 /*
- * Draw an octopus merge and return the number of characters written.
+ * Draw the horizontal dashes of an octopus merge and return the number of
+ * characters written.
  */
 static int graph_draw_octopus_merge(struct git_graph *graph,
                                    struct strbuf *sb)
 {
        /*
-        * Here dashless_commits represents the number of parents
-        * which don't need to have dashes (because their edges fit
-        * neatly under the commit).
-        */
-       const int dashless_commits = 2;
-       int col_num, i;
-       int num_dashes =
-               ((graph->num_parents - dashless_commits) * 2) - 1;
-       for (i = 0; i < num_dashes; i++) {
-               col_num = (i / 2) + dashless_commits + graph->commit_index;
-               strbuf_write_column(sb, &graph->new_columns[col_num], '-');
+        * Here dashless_parents represents the number of parents which don't
+        * need to have dashes (the edges labeled "0" and "1").  And
+        * dashful_parents are the remaining ones.
+        *
+        * | *---.
+        * | |\ \ \
+        * | | | | |
+        * x 0 1 2 3
+        *
+        */
+       const int dashless_parents = 2;
+       int dashful_parents = graph->num_parents - dashless_parents;
+
+       /*
+        * Usually, we add one new column for each parent (like the diagram
+        * above) but sometimes the first parent goes into an existing column,
+        * like this:
+        *
+        * | *---.
+        * | |\ \ \
+        * |/ / / /
+        * x 0 1 2
+        *
+        * In which case the number of parents will be one greater than the
+        * number of added columns.
+        */
+       int added_cols = (graph->num_new_columns - graph->num_columns);
+       int parent_in_old_cols = graph->num_parents - added_cols;
+
+       /*
+        * In both cases, commit_index corresponds to the edge labeled "0".
+        */
+       int first_col = graph->commit_index + dashless_parents
+           - parent_in_old_cols;
+
+       int i;
+       for (i = 0; i < dashful_parents; i++) {
+               strbuf_write_column(sb, &graph->new_columns[i+first_col], '-');
+               strbuf_write_column(sb, &graph->new_columns[i+first_col],
+                                   i == dashful_parents-1 ? '.' : '-');
        }
-       col_num = (i / 2) + dashless_commits + graph->commit_index;
-       strbuf_write_column(sb, &graph->new_columns[col_num], '.');
-       return num_dashes + 1;
+       return 2 * dashful_parents;
 }
 
 static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
diff --git a/ident.c b/ident.c
index 327abe5..33bcf40 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -168,6 +168,9 @@ const char *ident_default_email(void)
                        strbuf_addstr(&git_default_email, email);
                        committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
                        author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
+               } else if ((email = query_user_email()) && email[0]) {
+                       strbuf_addstr(&git_default_email, email);
+                       free((char *)email);
                } else
                        copy_email(xgetpwuid_self(&default_email_is_bogus),
                                   &git_default_email, &default_email_is_bogus);
diff --git a/khash.h b/khash.h
index d10caa0..532109c 100644 (file)
--- a/khash.h
+++ b/khash.h
@@ -234,7 +234,7 @@ static const double __ac_HASH_UPPER = 0.77;
        __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
 
 #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
-       KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+       KHASH_INIT2(name, MAYBE_UNUSED static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
 
 /* Other convenient macros... */
 
index c0e2bd6..e8da2e8 100644 (file)
@@ -30,7 +30,6 @@ static int gently_parse_list_objects_filter(
 
        if (filter_options->choice) {
                if (errbuf) {
-                       strbuf_init(errbuf, 0);
                        strbuf_addstr(
                                errbuf,
                                _("multiple filter-specs cannot be combined"));
@@ -50,6 +49,19 @@ static int gently_parse_list_objects_filter(
                        return 0;
                }
 
+       } else if (skip_prefix(arg, "tree:", &v0)) {
+               unsigned long depth;
+               if (!git_parse_ulong(v0, &depth) || depth != 0) {
+                       if (errbuf) {
+                               strbuf_addstr(
+                                       errbuf,
+                                       _("only 'tree:0' is supported"));
+                       }
+                       return 1;
+               }
+               filter_options->choice = LOFC_TREE_NONE;
+               return 0;
+
        } else if (skip_prefix(arg, "sparse:oid=", &v0)) {
                struct object_context oc;
                struct object_id sparse_oid;
@@ -71,10 +83,9 @@ static int gently_parse_list_objects_filter(
                return 0;
        }
 
-       if (errbuf) {
-               strbuf_init(errbuf, 0);
+       if (errbuf)
                strbuf_addf(errbuf, "invalid filter-spec '%s'", arg);
-       }
+
        memset(filter_options, 0, sizeof(*filter_options));
        return 1;
 }
index 0000a61..af64e5c 100644 (file)
@@ -10,6 +10,7 @@ enum list_objects_filter_choice {
        LOFC_DISABLED = 0,
        LOFC_BLOB_NONE,
        LOFC_BLOB_LIMIT,
+       LOFC_TREE_NONE,
        LOFC_SPARSE_OID,
        LOFC_SPARSE_PATH,
        LOFC__COUNT /* must be last */
index a0ba78b..765f3df 100644 (file)
@@ -44,8 +44,7 @@ static enum list_objects_filter_result filter_blobs_none(
 
        switch (filter_situation) {
        default:
-               die("unknown filter_situation");
-               return LOFR_ZERO;
+               BUG("unknown filter_situation: %d", filter_situation);
 
        case LOFS_BEGIN_TREE:
                assert(obj->type == OBJ_TREE);
@@ -80,6 +79,61 @@ static void *filter_blobs_none__init(
        return d;
 }
 
+/*
+ * A filter for list-objects to omit ALL trees and blobs from the traversal.
+ * Can OPTIONALLY collect a list of the omitted OIDs.
+ */
+struct filter_trees_none_data {
+       struct oidset *omits;
+};
+
+static enum list_objects_filter_result filter_trees_none(
+       enum list_objects_filter_situation filter_situation,
+       struct object *obj,
+       const char *pathname,
+       const char *filename,
+       void *filter_data_)
+{
+       struct filter_trees_none_data *filter_data = filter_data_;
+
+       switch (filter_situation) {
+       default:
+               BUG("unknown filter_situation: %d", filter_situation);
+
+       case LOFS_BEGIN_TREE:
+       case LOFS_BLOB:
+               if (filter_data->omits) {
+                       oidset_insert(filter_data->omits, &obj->oid);
+                       /* _MARK_SEEN but not _DO_SHOW (hard omit) */
+                       return LOFR_MARK_SEEN;
+               } else {
+                       /*
+                        * Not collecting omits so no need to to traverse tree.
+                        */
+                       return LOFR_SKIP_TREE | LOFR_MARK_SEEN;
+               }
+
+       case LOFS_END_TREE:
+               assert(obj->type == OBJ_TREE);
+               return LOFR_ZERO;
+
+       }
+}
+
+static void* filter_trees_none__init(
+       struct oidset *omitted,
+       struct list_objects_filter_options *filter_options,
+       filter_object_fn *filter_fn,
+       filter_free_fn *filter_free_fn)
+{
+       struct filter_trees_none_data *d = xcalloc(1, sizeof(*d));
+       d->omits = omitted;
+
+       *filter_fn = filter_trees_none;
+       *filter_free_fn = free;
+       return d;
+}
+
 /*
  * A filter for list-objects to omit large blobs.
  * And to OPTIONALLY collect a list of the omitted OIDs.
@@ -102,8 +156,7 @@ static enum list_objects_filter_result filter_blobs_limit(
 
        switch (filter_situation) {
        default:
-               die("unknown filter_situation");
-               return LOFR_ZERO;
+               BUG("unknown filter_situation: %d", filter_situation);
 
        case LOFS_BEGIN_TREE:
                assert(obj->type == OBJ_TREE);
@@ -208,8 +261,7 @@ static enum list_objects_filter_result filter_sparse(
 
        switch (filter_situation) {
        default:
-               die("unknown filter_situation");
-               return LOFR_ZERO;
+               BUG("unknown filter_situation: %d", filter_situation);
 
        case LOFS_BEGIN_TREE:
                assert(obj->type == OBJ_TREE);
@@ -374,6 +426,7 @@ static filter_init_fn s_filters[] = {
        NULL,
        filter_blobs_none__init,
        filter_blobs_limit__init,
+       filter_trees_none__init,
        filter_sparse_oid__init,
        filter_sparse_path__init,
 };
@@ -389,7 +442,7 @@ void *list_objects_filter__init(
        assert((sizeof(s_filters) / sizeof(s_filters[0])) == LOFC__COUNT);
 
        if (filter_options->choice >= LOFC__COUNT)
-               die("invalid list-objects filter choice: %d",
+               BUG("invalid list-objects filter choice: %d",
                    filter_options->choice);
 
        init_fn = s_filters[filter_options->choice];
index a6f6b49..52b4a84 100644 (file)
@@ -24,6 +24,11 @@ struct oidset;
  *              In general, objects should only be shown once, but
  *              this result DOES NOT imply that we mark it SEEN.
  *
+ * _SKIP_TREE : Used in LOFS_BEGIN_TREE situation - indicates that
+ *              the tree's children should not be iterated over. This
+ *              is used as an optimization when all children will
+ *              definitely be ignored.
+ *
  * Most of the time, you want the combination (_MARK_SEEN | _DO_SHOW)
  * but they can be used independently, such as when sparse-checkout
  * pattern matching is being applied.
@@ -45,6 +50,7 @@ enum list_objects_filter_result {
        LOFR_ZERO      = 0,
        LOFR_MARK_SEEN = 1<<0,
        LOFR_DO_SHOW   = 1<<1,
+       LOFR_SKIP_TREE = 1<<2,
 };
 
 enum list_objects_filter_situation {
index 0c2989d..c41cc80 100644 (file)
 #include "list-objects-filter-options.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "trace.h"
 
-static void process_blob(struct rev_info *revs,
+struct traversal_context {
+       struct rev_info *revs;
+       show_object_fn show_object;
+       show_commit_fn show_commit;
+       void *show_data;
+       filter_object_fn filter_fn;
+       void *filter_data;
+};
+
+static void process_blob(struct traversal_context *ctx,
                         struct blob *blob,
-                        show_object_fn show,
                         struct strbuf *path,
-                        const char *name,
-                        void *cb_data,
-                        filter_object_fn filter_fn,
-                        void *filter_data)
+                        const char *name)
 {
        struct object *obj = &blob->object;
        size_t pathlen;
        enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
 
-       if (!revs->blob_objects)
+       if (!ctx->revs->blob_objects)
                return;
        if (!obj)
                die("bad blob object");
@@ -41,21 +47,21 @@ static void process_blob(struct rev_info *revs,
         * may cause the actual filter to report an incomplete list
         * of missing objects.
         */
-       if (revs->exclude_promisor_objects &&
+       if (ctx->revs->exclude_promisor_objects &&
            !has_object_file(&obj->oid) &&
            is_promisor_object(&obj->oid))
                return;
 
        pathlen = path->len;
        strbuf_addstr(path, name);
-       if (!(obj->flags & USER_GIVEN) && filter_fn)
-               r = filter_fn(LOFS_BLOB, obj,
-                             path->buf, &path->buf[pathlen],
-                             filter_data);
+       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
+               r = ctx->filter_fn(LOFS_BLOB, obj,
+                                  path->buf, &path->buf[pathlen],
+                                  ctx->filter_data);
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
-               show(obj, path->buf, cb_data);
+               ctx->show_object(obj, path->buf, ctx->show_data);
        strbuf_setlen(path, pathlen);
 }
 
@@ -81,34 +87,66 @@ static void process_blob(struct rev_info *revs,
  * the link, and how to do it. Whether it necessarily makes
  * any sense what-so-ever to ever do that is another issue.
  */
-static void process_gitlink(struct rev_info *revs,
+static void process_gitlink(struct traversal_context *ctx,
                            const unsigned char *sha1,
-                           show_object_fn show,
                            struct strbuf *path,
-                           const char *name,
-                           void *cb_data)
+                           const char *name)
 {
        /* Nothing to do */
 }
 
-static void process_tree(struct rev_info *revs,
+static void process_tree(struct traversal_context *ctx,
                         struct tree *tree,
-                        show_object_fn show,
                         struct strbuf *base,
-                        const char *name,
-                        void *cb_data,
-                        filter_object_fn filter_fn,
-                        void *filter_data)
+                        const char *name);
+
+static void process_tree_contents(struct traversal_context *ctx,
+                                 struct tree *tree,
+                                 struct strbuf *base)
 {
-       struct object *obj = &tree->object;
        struct tree_desc desc;
        struct name_entry entry;
-       enum interesting match = revs->diffopt.pathspec.nr == 0 ?
-               all_entries_interesting: entry_not_interesting;
+       enum interesting match = ctx->revs->diffopt.pathspec.nr == 0 ?
+               all_entries_interesting : entry_not_interesting;
+
+       init_tree_desc(&desc, tree->buffer, tree->size);
+
+       while (tree_entry(&desc, &entry)) {
+               if (match != all_entries_interesting) {
+                       match = tree_entry_interesting(&entry, base, 0,
+                                                      &ctx->revs->diffopt.pathspec);
+                       if (match == all_entries_not_interesting)
+                               break;
+                       if (match == entry_not_interesting)
+                               continue;
+               }
+
+               if (S_ISDIR(entry.mode)) {
+                       struct tree *t = lookup_tree(the_repository, entry.oid);
+                       t->object.flags |= NOT_USER_GIVEN;
+                       process_tree(ctx, t, base, entry.path);
+               }
+               else if (S_ISGITLINK(entry.mode))
+                       process_gitlink(ctx, entry.oid->hash,
+                                       base, entry.path);
+               else {
+                       struct blob *b = lookup_blob(the_repository, entry.oid);
+                       b->object.flags |= NOT_USER_GIVEN;
+                       process_blob(ctx, b, base, entry.path);
+               }
+       }
+}
+
+static void process_tree(struct traversal_context *ctx,
+                        struct tree *tree,
+                        struct strbuf *base,
+                        const char *name)
+{
+       struct object *obj = &tree->object;
+       struct rev_info *revs = ctx->revs;
        int baselen = base->len;
        enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
-       int gently = revs->ignore_missing_links ||
-                    revs->exclude_promisor_objects;
+       int failed_parse;
 
        if (!revs->tree_objects)
                return;
@@ -116,7 +154,9 @@ static void process_tree(struct rev_info *revs,
                die("bad tree object");
        if (obj->flags & (UNINTERESTING | SEEN))
                return;
-       if (parse_tree_gently(tree, gently) < 0) {
+
+       failed_parse = parse_tree_gently(tree, 1);
+       if (failed_parse) {
                if (revs->ignore_missing_links)
                        return;
 
@@ -129,57 +169,35 @@ static void process_tree(struct rev_info *revs,
                    is_promisor_object(&obj->oid))
                        return;
 
-               die("bad tree object %s", oid_to_hex(&obj->oid));
+               if (!revs->do_not_die_on_missing_tree)
+                       die("bad tree object %s", oid_to_hex(&obj->oid));
        }
 
        strbuf_addstr(base, name);
-       if (!(obj->flags & USER_GIVEN) && filter_fn)
-               r = filter_fn(LOFS_BEGIN_TREE, obj,
-                             base->buf, &base->buf[baselen],
-                             filter_data);
+       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
+               r = ctx->filter_fn(LOFS_BEGIN_TREE, obj,
+                                  base->buf, &base->buf[baselen],
+                                  ctx->filter_data);
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
-               show(obj, base->buf, cb_data);
+               ctx->show_object(obj, base->buf, ctx->show_data);
        if (base->len)
                strbuf_addch(base, '/');
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       if (r & LOFR_SKIP_TREE)
+               trace_printf("Skipping contents of tree %s...\n", base->buf);
+       else if (!failed_parse)
+               process_tree_contents(ctx, tree, base);
 
-       while (tree_entry(&desc, &entry)) {
-               if (match != all_entries_interesting) {
-                       match = tree_entry_interesting(&entry, base, 0,
-                                                      &revs->diffopt.pathspec);
-                       if (match == all_entries_not_interesting)
-                               break;
-                       if (match == entry_not_interesting)
-                               continue;
-               }
-
-               if (S_ISDIR(entry.mode))
-                       process_tree(revs,
-                                    lookup_tree(the_repository, entry.oid),
-                                    show, base, entry.path,
-                                    cb_data, filter_fn, filter_data);
-               else if (S_ISGITLINK(entry.mode))
-                       process_gitlink(revs, entry.oid->hash,
-                                       show, base, entry.path,
-                                       cb_data);
-               else
-                       process_blob(revs,
-                                    lookup_blob(the_repository, entry.oid),
-                                    show, base, entry.path,
-                                    cb_data, filter_fn, filter_data);
-       }
-
-       if (!(obj->flags & USER_GIVEN) && filter_fn) {
-               r = filter_fn(LOFS_END_TREE, obj,
-                             base->buf, &base->buf[baselen],
-                             filter_data);
+       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn) {
+               r = ctx->filter_fn(LOFS_END_TREE, obj,
+                                  base->buf, &base->buf[baselen],
+                                  ctx->filter_data);
                if (r & LOFR_MARK_SEEN)
                        obj->flags |= SEEN;
                if (r & LOFR_DO_SHOW)
-                       show(obj, base->buf, cb_data);
+                       ctx->show_object(obj, base->buf, ctx->show_data);
        }
 
        strbuf_setlen(base, baselen);
@@ -244,19 +262,15 @@ static void add_pending_tree(struct rev_info *revs, struct tree *tree)
        add_pending_object(revs, &tree->object, "");
 }
 
-static void traverse_trees_and_blobs(struct rev_info *revs,
-                                    struct strbuf *base,
-                                    show_object_fn show_object,
-                                    void *show_data,
-                                    filter_object_fn filter_fn,
-                                    void *filter_data)
+static void traverse_trees_and_blobs(struct traversal_context *ctx,
+                                    struct strbuf *base)
 {
        int i;
 
        assert(base->len == 0);
 
-       for (i = 0; i < revs->pending.nr; i++) {
-               struct object_array_entry *pending = revs->pending.objects + i;
+       for (i = 0; i < ctx->revs->pending.nr; i++) {
+               struct object_array_entry *pending = ctx->revs->pending.objects + i;
                struct object *obj = pending->item;
                const char *name = pending->name;
                const char *path = pending->path;
@@ -264,62 +278,52 @@ static void traverse_trees_and_blobs(struct rev_info *revs,
                        continue;
                if (obj->type == OBJ_TAG) {
                        obj->flags |= SEEN;
-                       show_object(obj, name, show_data);
+                       ctx->show_object(obj, name, ctx->show_data);
                        continue;
                }
                if (!path)
                        path = "";
                if (obj->type == OBJ_TREE) {
-                       process_tree(revs, (struct tree *)obj, show_object,
-                                    base, path, show_data,
-                                    filter_fn, filter_data);
+                       process_tree(ctx, (struct tree *)obj, base, path);
                        continue;
                }
                if (obj->type == OBJ_BLOB) {
-                       process_blob(revs, (struct blob *)obj, show_object,
-                                    base, path, show_data,
-                                    filter_fn, filter_data);
+                       process_blob(ctx, (struct blob *)obj, base, path);
                        continue;
                }
                die("unknown pending object %s (%s)",
                    oid_to_hex(&obj->oid), name);
        }
-       object_array_clear(&revs->pending);
+       object_array_clear(&ctx->revs->pending);
 }
 
-static void do_traverse(struct rev_info *revs,
-                       show_commit_fn show_commit,
-                       show_object_fn show_object,
-                       void *show_data,
-                       filter_object_fn filter_fn,
-                       void *filter_data)
+static void do_traverse(struct traversal_context *ctx)
 {
        struct commit *commit;
        struct strbuf csp; /* callee's scratch pad */
        strbuf_init(&csp, PATH_MAX);
 
-       while ((commit = get_revision(revs)) != NULL) {
+       while ((commit = get_revision(ctx->revs)) != NULL) {
                /*
                 * an uninteresting boundary commit may not have its tree
                 * parsed yet, but we are not going to show them anyway
                 */
-               if (get_commit_tree(commit))
-                       add_pending_tree(revs, get_commit_tree(commit));
-               show_commit(commit, show_data);
+               if (get_commit_tree(commit)) {
+                       struct tree *tree = get_commit_tree(commit);
+                       tree->object.flags |= NOT_USER_GIVEN;
+                       add_pending_tree(ctx->revs, tree);
+               }
+               ctx->show_commit(commit, ctx->show_data);
 
-               if (revs->tree_blobs_in_commit_order)
+               if (ctx->revs->tree_blobs_in_commit_order)
                        /*
                         * NEEDSWORK: Adding the tree and then flushing it here
                         * needs a reallocation for each commit. Can we pass the
                         * tree directory without allocation churn?
                         */
-                       traverse_trees_and_blobs(revs, &csp,
-                                                show_object, show_data,
-                                                filter_fn, filter_data);
+                       traverse_trees_and_blobs(ctx, &csp);
        }
-       traverse_trees_and_blobs(revs, &csp,
-                                show_object, show_data,
-                                filter_fn, filter_data);
+       traverse_trees_and_blobs(ctx, &csp);
        strbuf_release(&csp);
 }
 
@@ -328,7 +332,14 @@ void traverse_commit_list(struct rev_info *revs,
                          show_object_fn show_object,
                          void *show_data)
 {
-       do_traverse(revs, show_commit, show_object, show_data, NULL, NULL);
+       struct traversal_context ctx;
+       ctx.revs = revs;
+       ctx.show_commit = show_commit;
+       ctx.show_object = show_object;
+       ctx.show_data = show_data;
+       ctx.filter_fn = NULL;
+       ctx.filter_data = NULL;
+       do_traverse(&ctx);
 }
 
 void traverse_commit_list_filtered(
@@ -339,14 +350,18 @@ void traverse_commit_list_filtered(
        void *show_data,
        struct oidset *omitted)
 {
-       filter_object_fn filter_fn = NULL;
+       struct traversal_context ctx;
        filter_free_fn filter_free_fn = NULL;
-       void *filter_data = NULL;
-
-       filter_data = list_objects_filter__init(omitted, filter_options,
-                                               &filter_fn, &filter_free_fn);
-       do_traverse(revs, show_commit, show_object, show_data,
-                   filter_fn, filter_data);
-       if (filter_data && filter_free_fn)
-               filter_free_fn(filter_data);
+
+       ctx.revs = revs;
+       ctx.show_object = show_object;
+       ctx.show_commit = show_commit;
+       ctx.show_data = show_data;
+       ctx.filter_fn = NULL;
+
+       ctx.filter_data = list_objects_filter__init(omitted, filter_options,
+                                                   &ctx.filter_fn, &filter_free_fn);
+       do_traverse(&ctx);
+       if (ctx.filter_data && filter_free_fn)
+               filter_free_fn(ctx.filter_data);
 }
diff --git a/midx.c b/midx.c
index ea2f3ff..4fac0cd 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -941,7 +941,7 @@ static void midx_report(const char *fmt, ...)
 int verify_midx_file(const char *object_dir)
 {
        uint32_t i;
-       struct progress *progress = NULL;
+       struct progress *progress;
        struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
        verify_midx_error = 0;
 
diff --git a/midx.h b/midx.h
index 2d7c9c6..1d6c21a 100644 (file)
--- a/midx.h
+++ b/midx.h
@@ -1,5 +1,5 @@
-#ifndef __MIDX_H__
-#define __MIDX_H__
+#ifndef MIDX_H
+#define MIDX_H
 
 #include "repository.h"
 
index f2fd9d8..9d1b951 100644 (file)
@@ -37,7 +37,7 @@ struct bitmap_writer {
 
        struct progress *progress;
        int show_progress;
-       unsigned char pack_checksum[20];
+       unsigned char pack_checksum[GIT_MAX_RAWSZ];
 };
 
 static struct bitmap_writer writer;
index 7e624c3..b6cdbb0 100644 (file)
@@ -148,6 +148,9 @@ void prepare_packing_data(struct packing_data *pdata)
                                             1U << OE_SIZE_BITS);
        pdata->oe_delta_size_limit = git_env_ulong("GIT_TEST_OE_DELTA_SIZE",
                                                   1UL << OE_DELTA_SIZE_BITS);
+#ifndef NO_PTHREADS
+       pthread_mutex_init(&pdata->lock, NULL);
+#endif
 }
 
 struct object_entry *packlist_alloc(struct packing_data *pdata,
index 2ca39cf..86ee93f 100644 (file)
@@ -377,7 +377,7 @@ static inline unsigned long oe_delta_size(struct packing_data *pack,
                return e->delta_size_;
 
        /*
-        * pack->detla_size[] can't be NULL because oe_set_delta_size()
+        * pack->delta_size[] can't be NULL because oe_set_delta_size()
         * must have been called when a new delta is saved with
         * oe_set_delta().
         * If oe_delta() returns NULL (i.e. default state, which means
index bb521cf..3c58784 100644 (file)
@@ -122,13 +122,14 @@ static void create_pack_revindex(struct packed_git *p)
        unsigned num_ent = p->num_objects;
        unsigned i;
        const char *index = p->index_data;
+       const unsigned hashsz = the_hash_algo->rawsz;
 
        ALLOC_ARRAY(p->revindex, num_ent + 1);
        index += 4 * 256;
 
        if (p->index_version > 1) {
                const uint32_t *off_32 =
-                       (uint32_t *)(index + 8 + p->num_objects * (20 + 4));
+                       (uint32_t *)(index + 8 + p->num_objects * (hashsz + 4));
                const uint32_t *off_64 = off_32 + p->num_objects;
                for (i = 0; i < num_ent; i++) {
                        uint32_t off = ntohl(*off_32++);
@@ -142,16 +143,17 @@ static void create_pack_revindex(struct packed_git *p)
                }
        } else {
                for (i = 0; i < num_ent; i++) {
-                       uint32_t hl = *((uint32_t *)(index + 24 * i));
+                       uint32_t hl = *((uint32_t *)(index + (hashsz + 4) * i));
                        p->revindex[i].offset = ntohl(hl);
                        p->revindex[i].nr = i;
                }
        }
 
-       /* This knows the pack format -- the 20-byte trailer
+       /*
+        * This knows the pack format -- the hash trailer
         * follows immediately after the last object data.
         */
-       p->revindex[num_ent].offset = p->pack_size - 20;
+       p->revindex[num_ent].offset = p->pack_size - hashsz;
        p->revindex[num_ent].nr = -1;
        sort_revindex(p->revindex, num_ent, p->pack_size);
 }
index 841b361..f2850a0 100644 (file)
@@ -80,10 +80,8 @@ void pack_report(void)
 static int check_packed_git_idx(const char *path, struct packed_git *p)
 {
        void *idx_map;
-       struct pack_idx_header *hdr;
        size_t idx_size;
-       uint32_t version, nr, i, *index;
-       int fd = git_open(path);
+       int fd = git_open(path), ret;
        struct stat st;
        const unsigned int hashsz = the_hash_algo->rawsz;
 
@@ -101,16 +99,32 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
 
-       hdr = idx_map;
+       ret = load_idx(path, hashsz, idx_map, idx_size, p);
+
+       if (ret)
+               munmap(idx_map, idx_size);
+
+       return ret;
+}
+
+int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+            size_t idx_size, struct packed_git *p)
+{
+       struct pack_idx_header *hdr = idx_map;
+       uint32_t version, nr, i, *index;
+
+       if (idx_size < 4 * 256 + hashsz + hashsz)
+               return error("index file %s is too small", path);
+       if (idx_map == NULL)
+               return error("empty data");
+
        if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
                version = ntohl(hdr->idx_version);
-               if (version < 2 || version > 2) {
-                       munmap(idx_map, idx_size);
+               if (version < 2 || version > 2)
                        return error("index file %s is version %"PRIu32
                                     " and is not supported by this binary"
                                     " (try upgrading GIT to a newer version)",
                                     path, version);
-               }
        } else
                version = 1;
 
@@ -120,10 +134,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                index += 2;  /* skip index header */
        for (i = 0; i < 256; i++) {
                uint32_t n = ntohl(index[i]);
-               if (n < nr) {
-                       munmap(idx_map, idx_size);
+               if (n < nr)
                        return error("non-monotonic index %s", path);
-               }
                nr = n;
        }
 
@@ -135,10 +147,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                 *  - hash of the packfile
                 *  - file checksum
                 */
-               if (idx_size != 4*256 + nr * (hashsz + 4) + hashsz + hashsz) {
-                       munmap(idx_map, idx_size);
+               if (idx_size != 4 * 256 + nr * (hashsz + 4) + hashsz + hashsz)
                        return error("wrong index v1 file size in %s", path);
-               }
        } else if (version == 2) {
                /*
                 * Minimum size:
@@ -157,20 +167,16 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                unsigned long max_size = min_size;
                if (nr)
                        max_size += (nr - 1)*8;
-               if (idx_size < min_size || idx_size > max_size) {
-                       munmap(idx_map, idx_size);
+               if (idx_size < min_size || idx_size > max_size)
                        return error("wrong index v2 file size in %s", path);
-               }
                if (idx_size != min_size &&
                    /*
                     * make sure we can deal with large pack offsets.
                     * 31-bit signed offset won't be enough, neither
                     * 32-bit unsigned one will be.
                     */
-                   (sizeof(off_t) <= 4)) {
-                       munmap(idx_map, idx_size);
+                   (sizeof(off_t) <= 4))
                        return error("pack too large for current definition of off_t in %s", path);
-               }
        }
 
        p->index_version = version;
@@ -1121,13 +1127,14 @@ int unpack_object_header(struct packed_git *p,
 void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1)
 {
        unsigned i;
+       const unsigned hashsz = the_hash_algo->rawsz;
        for (i = 0; i < p->num_bad_objects; i++)
-               if (hasheq(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
+               if (hasheq(sha1, p->bad_object_sha1 + hashsz * i))
                        return;
        p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
                                      st_mult(GIT_MAX_RAWSZ,
                                              st_add(p->num_bad_objects, 1)));
-       hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
+       hashcpy(p->bad_object_sha1 + hashsz * p->num_bad_objects, sha1);
        p->num_bad_objects++;
 }
 
index 4426257..6c40376 100644 (file)
@@ -164,4 +164,17 @@ extern int has_pack_index(const unsigned char *sha1);
  */
 extern int is_promisor_object(const struct object_id *oid);
 
+/*
+ * Expose a function for fuzz testing.
+ *
+ * load_idx() parses a block of memory as a packfile index and puts the results
+ * into a struct packed_git.
+ *
+ * This function should not be used directly. It is exposed here only so that we
+ * have a convenient entry-point for fuzz testing. For real uses, you should
+ * probably use open_pack_index() or parse_pack_index() instead.
+ */
+extern int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+                   size_t idx_size, struct packed_git *p);
+
 #endif
index 60edb2f..bd8083f 100644 (file)
@@ -354,7 +354,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)
 {
        struct diff_filespec *spec = alloc_filespec(name);
 
-       fill_filespec(spec, &null_oid, 0, 0644);
+       fill_filespec(spec, &null_oid, 0, 0100644);
        spec->data = (char *)p;
        spec->size = strlen(p);
        spec->should_munmap = 0;
index 1df5c16..d579582 100644 (file)
@@ -345,7 +345,7 @@ static int is_racy_stat(const struct index_state *istate,
                );
 }
 
-static int is_racy_timestamp(const struct index_state *istate,
+int is_racy_timestamp(const struct index_state *istate,
                             const struct cache_entry *ce)
 {
        return (!S_ISGITLINK(ce->ce_mode) &&
index 2a05619..0c45ed9 100644 (file)
@@ -875,7 +875,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
                if (deref)
                        name++;
                if (!strcmp(name, "objecttype"))
-                       v->s = type_name(oi->type);
+                       v->s = xstrdup(type_name(oi->type));
                else if (!strcmp(name, "objectsize")) {
                        v->value = oi->size;
                        v->s = xstrfmt("%lu", oi->size);
@@ -899,9 +899,9 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
                if (deref)
                        name++;
                if (!strcmp(name, "tag"))
-                       v->s = tag->tag;
+                       v->s = xstrdup(tag->tag);
                else if (!strcmp(name, "type") && tag->tagged)
-                       v->s = type_name(tag->tagged->type);
+                       v->s = xstrdup(type_name(tag->tagged->type));
                else if (!strcmp(name, "object") && tag->tagged)
                        v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
        }
@@ -1032,7 +1032,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
        v->value = timestamp;
        return;
  bad:
-       v->s = "";
+       v->s = xstrdup("");
        v->value = 0;
 }
 
@@ -1227,7 +1227,7 @@ static void fill_missing_values(struct atom_value *val)
        for (i = 0; i < used_atom_cnt; i++) {
                struct atom_value *v = &val[i];
                if (v->s == NULL)
-                       v->s = "";
+                       v->s = xstrdup("");
        }
 }
 
@@ -1273,7 +1273,8 @@ static inline char *copy_advance(char *dst, const char *src)
 static const char *lstrip_ref_components(const char *refname, int len)
 {
        long remaining = len;
-       const char *start = refname;
+       const char *start = xstrdup(refname);
+       const char *to_free = start;
 
        if (len < 0) {
                int i;
@@ -1294,20 +1295,24 @@ static const char *lstrip_ref_components(const char *refname, int len)
        while (remaining > 0) {
                switch (*start++) {
                case '\0':
-                       return "";
+                       free((char *)to_free);
+                       return xstrdup("");
                case '/':
                        remaining--;
                        break;
                }
        }
 
+       start = xstrdup(start);
+       free((char *)to_free);
        return start;
 }
 
 static const char *rstrip_ref_components(const char *refname, int len)
 {
        long remaining = len;
-       char *start = xstrdup(refname);
+       const char *start = xstrdup(refname);
+       const char *to_free = start;
 
        if (len < 0) {
                int i;
@@ -1327,9 +1332,10 @@ static const char *rstrip_ref_components(const char *refname, int len)
 
        while (remaining-- > 0) {
                char *p = strrchr(start, '/');
-               if (p == NULL)
-                       return "";
-               else
+               if (p == NULL) {
+                       free((char *)to_free);
+                       return xstrdup("");
+               } else
                        p[0] = '\0';
        }
        return start;
@@ -1344,7 +1350,7 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)
        else if (atom->option == R_RSTRIP)
                return rstrip_ref_components(refname, atom->rstrip);
        else
-               return refname;
+               return xstrdup(refname);
 }
 
 static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
@@ -1358,7 +1364,7 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                                       NULL, AHEAD_BEHIND_FULL) < 0) {
                        *s = xstrdup(msgs.gone);
                } else if (!num_ours && !num_theirs)
-                       *s = "";
+                       *s = xstrdup("");
                else if (!num_ours)
                        *s = xstrfmt(msgs.behind, num_theirs);
                else if (!num_theirs)
@@ -1373,36 +1379,31 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                }
        } else if (atom->u.remote_ref.option == RR_TRACKSHORT) {
                if (stat_tracking_info(branch, &num_ours, &num_theirs,
-                                      NULL, AHEAD_BEHIND_FULL) < 0)
+                                      NULL, AHEAD_BEHIND_FULL) < 0) {
+                       *s = xstrdup("");
                        return;
-
+               }
                if (!num_ours && !num_theirs)
-                       *s = "=";
+                       *s = xstrdup("=");
                else if (!num_ours)
-                       *s = "<";
+                       *s = xstrdup("<");
                else if (!num_theirs)
-                       *s = ">";
+                       *s = xstrdup(">");
                else
-                       *s = "<>";
+                       *s = xstrdup("<>");
        } else if (atom->u.remote_ref.option == RR_REMOTE_NAME) {
                int explicit;
                const char *remote = atom->u.remote_ref.push ?
                        pushremote_for_branch(branch, &explicit) :
                        remote_for_branch(branch, &explicit);
-               if (explicit)
-                       *s = xstrdup(remote);
-               else
-                       *s = "";
+               *s = xstrdup(explicit ? remote : "");
        } else if (atom->u.remote_ref.option == RR_REMOTE_REF) {
                int explicit;
                const char *merge;
 
                merge = remote_ref_for_branch(branch, atom->u.remote_ref.push,
                                              &explicit);
-               if (explicit)
-                       *s = xstrdup(merge);
-               else
-                       *s = "";
+               *s = xstrdup(explicit ? merge : "");
        } else
                BUG("unhandled RR_* enum");
 }
@@ -1451,7 +1452,7 @@ char *get_head_description(void)
 static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)
 {
        if (!ref->symref)
-               return "";
+               return xstrdup("");
        else
                return show_ref(&atom->u.refname, ref->symref);
 }
@@ -1510,7 +1511,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
                                             NULL, NULL);
                if (!ref->symref)
-                       ref->symref = "";
+                       ref->symref = xstrdup("");
        }
 
        /* Fill in specials first */
@@ -1536,20 +1537,23 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        refname = get_symref(atom, ref);
                else if (starts_with(name, "upstream")) {
                        const char *branch_name;
-                       v->s = "";
                        /* only local branches may have an upstream */
                        if (!skip_prefix(ref->refname, "refs/heads/",
-                                        &branch_name))
+                                        &branch_name)) {
+                               v->s = xstrdup("");
                                continue;
+                       }
                        branch = branch_get(branch_name);
 
                        refname = branch_get_upstream(branch, NULL);
                        if (refname)
                                fill_remote_ref_details(atom, refname, branch, &v->s);
+                       else
+                               v->s = xstrdup("");
                        continue;
                } else if (atom->u.remote_ref.push) {
                        const char *branch_name;
-                       v->s = "";
+                       v->s = xstrdup("");
                        if (!skip_prefix(ref->refname, "refs/heads/",
                                         &branch_name))
                                continue;
@@ -1562,10 +1566,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                                if (!refname)
                                        continue;
                        }
+                       /* We will definitely re-init v->s on the next line. */
+                       free((char *)v->s);
                        fill_remote_ref_details(atom, refname, branch, &v->s);
                        continue;
                } else if (starts_with(name, "color:")) {
-                       v->s = atom->u.color;
+                       v->s = xstrdup(atom->u.color);
                        continue;
                } else if (!strcmp(name, "flag")) {
                        char buf[256], *cp = buf;
@@ -1574,7 +1580,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        if (ref->flag & REF_ISPACKED)
                                cp = copy_advance(cp, ",packed");
                        if (cp == buf)
-                               v->s = "";
+                               v->s = xstrdup("");
                        else {
                                *cp = '\0';
                                v->s = xstrdup(buf + 1);
@@ -1584,40 +1590,42 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        continue;
                } else if (!strcmp(name, "HEAD")) {
                        if (atom->u.head && !strcmp(ref->refname, atom->u.head))
-                               v->s = "*";
+                               v->s = xstrdup("*");
                        else
-                               v->s = " ";
+                               v->s = xstrdup(" ");
                        continue;
                } else if (starts_with(name, "align")) {
                        v->handler = align_atom_handler;
-                       v->s = "";
+                       v->s = xstrdup("");
                        continue;
                } else if (!strcmp(name, "end")) {
                        v->handler = end_atom_handler;
-                       v->s = "";
+                       v->s = xstrdup("");
                        continue;
                } else if (starts_with(name, "if")) {
                        const char *s;
-                       v->s = "";
                        if (skip_prefix(name, "if:", &s))
                                v->s = xstrdup(s);
+                       else
+                               v->s = xstrdup("");
                        v->handler = if_atom_handler;
                        continue;
                } else if (!strcmp(name, "then")) {
                        v->handler = then_atom_handler;
-                       v->s = "";
+                       v->s = xstrdup("");
                        continue;
                } else if (!strcmp(name, "else")) {
                        v->handler = else_atom_handler;
-                       v->s = "";
+                       v->s = xstrdup("");
                        continue;
                } else
                        continue;
 
                if (!deref)
-                       v->s = refname;
+                       v->s = xstrdup(refname);
                else
                        v->s = xstrfmt("%s^{}", refname);
+               free((char *)refname);
        }
 
        for (i = 0; i < used_atom_cnt; i++) {
@@ -1988,6 +1996,10 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 static void free_array_item(struct ref_array_item *item)
 {
        free((char *)item->symref);
+       if (item->value) {
+               free((char *)item->value->s);
+               free(item->value);
+       }
        free(item);
 }
 
@@ -1996,6 +2008,10 @@ void ref_array_clear(struct ref_array *array)
 {
        int i;
 
+       for (i = 0; i < used_atom_cnt; i++)
+               free((char *)used_atom[i].name);
+       FREE_AND_NULL(used_atom);
+       used_atom_cnt = 0;
        for (i = 0; i < array->nr; i++)
                free_array_item(array->items[i]);
        FREE_AND_NULL(array->items);
index 74e2996..c01c7f5 100644 (file)
@@ -274,8 +274,8 @@ struct snapshot_record {
 static int cmp_packed_ref_records(const void *v1, const void *v2)
 {
        const struct snapshot_record *e1 = v1, *e2 = v2;
-       const char *r1 = e1->start + GIT_SHA1_HEXSZ + 1;
-       const char *r2 = e2->start + GIT_SHA1_HEXSZ + 1;
+       const char *r1 = e1->start + the_hash_algo->hexsz + 1;
+       const char *r2 = e2->start + the_hash_algo->hexsz + 1;
 
        while (1) {
                if (*r1 == '\n')
@@ -297,7 +297,7 @@ static int cmp_packed_ref_records(const void *v1, const void *v2)
  */
 static int cmp_record_to_refname(const char *rec, const char *refname)
 {
-       const char *r1 = rec + GIT_SHA1_HEXSZ + 1;
+       const char *r1 = rec + the_hash_algo->hexsz + 1;
        const char *r2 = refname;
 
        while (1) {
@@ -344,7 +344,7 @@ static void sort_snapshot(struct snapshot *snapshot)
                if (!eol)
                        /* The safety check should prevent this. */
                        BUG("unterminated line found in packed-refs");
-               if (eol - pos < GIT_SHA1_HEXSZ + 2)
+               if (eol - pos < the_hash_algo->hexsz + 2)
                        die_invalid_line(snapshot->refs->path,
                                         pos, eof - pos);
                eol++;
@@ -456,7 +456,7 @@ static void verify_buffer_safe(struct snapshot *snapshot)
                return;
 
        last_line = find_start_of_record(start, eof - 1);
-       if (*(eof - 1) != '\n' || eof - last_line < GIT_SHA1_HEXSZ + 2)
+       if (*(eof - 1) != '\n' || eof - last_line < the_hash_algo->hexsz + 2)
                die_invalid_line(snapshot->refs->path,
                                 last_line, eof - last_line);
 }
@@ -796,7 +796,7 @@ static int next_record(struct packed_ref_iterator *iter)
 
        iter->base.flags = REF_ISPACKED;
 
-       if (iter->eof - p < GIT_SHA1_HEXSZ + 2 ||
+       if (iter->eof - p < the_hash_algo->hexsz + 2 ||
            parse_oid_hex(p, &iter->oid, &p) ||
            !isspace(*p++))
                die_invalid_line(iter->snapshot->refs->path,
@@ -826,7 +826,7 @@ static int next_record(struct packed_ref_iterator *iter)
 
        if (iter->pos < iter->eof && *iter->pos == '^') {
                p = iter->pos + 1;
-               if (iter->eof - p < GIT_SHA1_HEXSZ + 1 ||
+               if (iter->eof - p < the_hash_algo->hexsz + 1 ||
                    parse_oid_hex(p, &iter->peeled, &p) ||
                    *p++ != '\n')
                        die_invalid_line(iter->snapshot->refs->path,
index 887e26d..b5b2357 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -29,7 +29,7 @@ static int rerere_dir_alloc;
 #define RR_HAS_POSTIMAGE 1
 #define RR_HAS_PREIMAGE 2
 static struct rerere_dir {
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_HEXSZ];
        int status_alloc, status_nr;
        unsigned char *status;
 } **rerere_dir;
@@ -52,7 +52,7 @@ static void free_rerere_id(struct string_list_item *item)
 
 static const char *rerere_id_hex(const struct rerere_id *id)
 {
-       return sha1_to_hex(id->collection->sha1);
+       return sha1_to_hex(id->collection->hash);
 }
 
 static void fit_variant(struct rerere_dir *rr_dir, int variant)
@@ -115,7 +115,7 @@ static int is_rr_file(const char *name, const char *filename, int *variant)
 static void scan_rerere_dir(struct rerere_dir *rr_dir)
 {
        struct dirent *de;
-       DIR *dir = opendir(git_path("rr-cache/%s", sha1_to_hex(rr_dir->sha1)));
+       DIR *dir = opendir(git_path("rr-cache/%s", sha1_to_hex(rr_dir->hash)));
 
        if (!dir)
                return;
@@ -133,24 +133,24 @@ static void scan_rerere_dir(struct rerere_dir *rr_dir)
        closedir(dir);
 }
 
-static const unsigned char *rerere_dir_sha1(size_t i, void *table)
+static const unsigned char *rerere_dir_hash(size_t i, void *table)
 {
        struct rerere_dir **rr_dir = table;
-       return rr_dir[i]->sha1;
+       return rr_dir[i]->hash;
 }
 
 static struct rerere_dir *find_rerere_dir(const char *hex)
 {
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_RAWSZ];
        struct rerere_dir *rr_dir;
        int pos;
 
-       if (get_sha1_hex(hex, sha1))
+       if (get_sha1_hex(hex, hash))
                return NULL; /* BUG */
-       pos = sha1_pos(sha1, rerere_dir, rerere_dir_nr, rerere_dir_sha1);
+       pos = sha1_pos(hash, rerere_dir, rerere_dir_nr, rerere_dir_hash);
        if (pos < 0) {
                rr_dir = xmalloc(sizeof(*rr_dir));
-               hashcpy(rr_dir->sha1, sha1);
+               hashcpy(rr_dir->hash, hash);
                rr_dir->status = NULL;
                rr_dir->status_nr = 0;
                rr_dir->status_alloc = 0;
@@ -207,26 +207,27 @@ static void read_rr(struct string_list *rr)
                return;
        while (!strbuf_getwholeline(&buf, in, '\0')) {
                char *path;
-               unsigned char sha1[20];
+               unsigned char hash[GIT_MAX_RAWSZ];
                struct rerere_id *id;
                int variant;
+               const unsigned hexsz = the_hash_algo->hexsz;
 
                /* There has to be the hash, tab, path and then NUL */
-               if (buf.len < 42 || get_sha1_hex(buf.buf, sha1))
+               if (buf.len < hexsz + 2 || get_sha1_hex(buf.buf, hash))
                        die(_("corrupt MERGE_RR"));
 
-               if (buf.buf[40] != '.') {
+               if (buf.buf[hexsz] != '.') {
                        variant = 0;
-                       path = buf.buf + 40;
+                       path = buf.buf + hexsz;
                } else {
                        errno = 0;
-                       variant = strtol(buf.buf + 41, &path, 10);
+                       variant = strtol(buf.buf + hexsz + 1, &path, 10);
                        if (errno)
                                die(_("corrupt MERGE_RR"));
                }
                if (*(path++) != '\t')
                        die(_("corrupt MERGE_RR"));
-               buf.buf[40] = '\0';
+               buf.buf[hexsz] = '\0';
                id = new_rerere_id_hex(buf.buf);
                id->variant = variant;
                string_list_insert(rr, path)->util = id;
@@ -360,7 +361,7 @@ static void rerere_strbuf_putconflict(struct strbuf *buf, int ch, size_t size)
 }
 
 static int handle_conflict(struct strbuf *out, struct rerere_io *io,
-                          int marker_size, git_SHA_CTX *ctx)
+                          int marker_size, git_hash_ctx *ctx)
 {
        enum {
                RR_SIDE_1 = 0, RR_SIDE_2, RR_ORIGINAL
@@ -398,10 +399,12 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
                        strbuf_addbuf(out, &two);
                        rerere_strbuf_putconflict(out, '>', marker_size);
                        if (ctx) {
-                               git_SHA1_Update(ctx, one.buf ? one.buf : "",
-                                           one.len + 1);
-                               git_SHA1_Update(ctx, two.buf ? two.buf : "",
-                                           two.len + 1);
+                               the_hash_algo->update_fn(ctx, one.buf ?
+                                                        one.buf : "",
+                                                        one.len + 1);
+                               the_hash_algo->update_fn(ctx, two.buf ?
+                                                        two.buf : "",
+                                                        two.len + 1);
                        }
                        break;
                } else if (hunk == RR_SIDE_1)
@@ -430,18 +433,18 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
  * Return 1 if conflict hunks are found, 0 if there are no conflict
  * hunks and -1 if an error occured.
  */
-static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size)
+static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_size)
 {
-       git_SHA_CTX ctx;
+       git_hash_ctx ctx;
        struct strbuf buf = STRBUF_INIT, out = STRBUF_INIT;
        int has_conflicts = 0;
-       if (sha1)
-               git_SHA1_Init(&ctx);
+       if (hash)
+               the_hash_algo->init_fn(&ctx);
 
        while (!io->getline(&buf, io)) {
                if (is_cmarker(buf.buf, '<', marker_size)) {
                        has_conflicts = handle_conflict(&out, io, marker_size,
-                                                       sha1 ? &ctx : NULL);
+                                                       hash ? &ctx : NULL);
                        if (has_conflicts < 0)
                                break;
                        rerere_io_putmem(out.buf, out.len, io);
@@ -452,8 +455,8 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
        strbuf_release(&buf);
        strbuf_release(&out);
 
-       if (sha1)
-               git_SHA1_Final(sha1, &ctx);
+       if (hash)
+               the_hash_algo->final_fn(hash, &ctx);
 
        return has_conflicts;
 }
@@ -462,8 +465,8 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
  * Scan the path for conflicts, do the "handle_path()" thing above, and
  * return the number of conflict hunks found.
  */
-static int handle_file(struct index_state *istate, const char *path,
-                      unsigned char *sha1, const char *output)
+static int handle_file(struct index_state *istate,
+                      const char *path, unsigned char *hash, const char *output)
 {
        int has_conflicts = 0;
        struct rerere_io_file io;
@@ -485,7 +488,7 @@ static int handle_file(struct index_state *istate, const char *path,
                }
        }
 
-       has_conflicts = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+       has_conflicts = handle_path(hash, (struct rerere_io *)&io, marker_size);
 
        fclose(io.input);
        if (io.io.wrerror)
@@ -821,7 +824,7 @@ static int do_plain_rerere(struct repository *r,
         */
        for (i = 0; i < conflict.nr; i++) {
                struct rerere_id *id;
-               unsigned char sha1[20];
+               unsigned char hash[GIT_MAX_RAWSZ];
                const char *path = conflict.items[i].string;
                int ret;
 
@@ -830,7 +833,7 @@ static int do_plain_rerere(struct repository *r,
                 * conflict ID.  No need to write anything out
                 * yet.
                 */
-               ret = handle_file(r->index, path, sha1, NULL);
+               ret = handle_file(r->index, path, hash, NULL);
                if (ret != 0 && string_list_has_string(rr, path)) {
                        remove_variant(string_list_lookup(rr, path)->util);
                        string_list_remove(rr, path, 1);
@@ -838,7 +841,7 @@ static int do_plain_rerere(struct repository *r,
                if (ret < 1)
                        continue;
 
-               id = new_rerere_id(sha1);
+               id = new_rerere_id(hash);
                string_list_insert(rr, path)->util = id;
 
                /* Ensure that the directory exists. */
@@ -949,8 +952,8 @@ static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
        return 0;
 }
 
-static int handle_cache(struct index_state *istate, const char *path,
-                       unsigned char *sha1, const char *output)
+static int handle_cache(struct index_state *istate,
+                       const char *path, unsigned char *hash, const char *output)
 {
        mmfile_t mmfile[3] = {{NULL}};
        mmbuffer_t result = {NULL, 0};
@@ -1010,7 +1013,7 @@ static int handle_cache(struct index_state *istate, const char *path,
         * Grab the conflict ID and optionally write the original
         * contents with conflict markers out.
         */
-       has_conflicts = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+       has_conflicts = handle_path(hash, (struct rerere_io *)&io, marker_size);
        strbuf_release(&io.input);
        if (io.io.output)
                fclose(io.io.output);
@@ -1023,7 +1026,7 @@ static int rerere_forget_one_path(struct index_state *istate,
 {
        const char *filename;
        struct rerere_id *id;
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_RAWSZ];
        int ret;
        struct string_list_item *item;
 
@@ -1031,12 +1034,12 @@ static int rerere_forget_one_path(struct index_state *istate,
         * Recreate the original conflict from the stages in the
         * index and compute the conflict ID
         */
-       ret = handle_cache(istate, path, sha1, NULL);
+       ret = handle_cache(istate, path, hash, NULL);
        if (ret < 1)
                return error(_("could not parse conflict hunks in '%s'"), path);
 
        /* Nuke the recorded resolution for the conflict */
-       id = new_rerere_id(sha1);
+       id = new_rerere_id(hash);
 
        for (id->variant = 0;
             id->variant < id->collection->status_nr;
@@ -1048,7 +1051,7 @@ static int rerere_forget_one_path(struct index_state *istate,
                if (!has_rerere_resolution(id))
                        continue;
 
-               handle_cache(istate, path, sha1, rerere_path(id, "thisimage"));
+               handle_cache(istate, path, hash, rerere_path(id, "thisimage"));
                if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                        free(cur.ptr);
                        error(_("failed to update conflicted state in '%s'"), path);
@@ -1080,7 +1083,7 @@ static int rerere_forget_one_path(struct index_state *istate,
         * conflict in the working tree, run us again to record
         * the postimage.
         */
-       handle_cache(istate, path, sha1, rerere_path(id, "preimage"));
+       handle_cache(istate, path, hash, rerere_path(id, "preimage"));
        fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
 
        /*
index b5108b7..a1ddb9e 100644 (file)
@@ -177,7 +177,6 @@ static void add_pending_object_with_path(struct rev_info *revs,
                strbuf_release(&buf);
                return; /* do not add the commit itself */
        }
-       obj->flags |= USER_GIVEN;
        add_object_array_with_path(obj, name, &revs->pending, mode, path);
 }
 
index bc30a30..1cd0c4b 100644 (file)
 #define SYMMETRIC_LEFT (1u<<8)
 #define PATCHSAME      (1u<<9)
 #define BOTTOM         (1u<<10)
-#define USER_GIVEN     (1u<<25) /* given directly by the user */
+/*
+ * Indicates object was reached by traversal. i.e. not given by user on
+ * command-line or stdin.
+ * NEEDSWORK: NOT_USER_GIVEN doesn't apply to commits because we only support
+ * filtering trees and blobs, but it may be useful to support filtering commits
+ * in the future.
+ */
+#define NOT_USER_GIVEN (1u<<25)
 #define TRACK_LINEAR   (1u<<26)
-#define ALL_REV_FLAGS  (((1u<<11)-1) | USER_GIVEN | TRACK_LINEAR)
+#define ALL_REV_FLAGS  (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR)
 
 #define DECORATE_SHORT_REFS    1
 #define DECORATE_FULL_REFS     2
@@ -133,6 +140,21 @@ struct rev_info {
                        line_level_traverse:1,
                        tree_blobs_in_commit_order:1,
 
+                       /*
+                        * Blobs are shown without regard for their existence.
+                        * But not so for trees: unless exclude_promisor_objects
+                        * is set and the tree in question is a promisor object;
+                        * OR ignore_missing_links is set, the revision walker
+                        * dies with a "bad tree object HASH" message when
+                        * encountering a missing tree. For callers that can
+                        * handle missing trees and want them to be filterable
+                        * and showable, set this to true. The revision walker
+                        * will filter and show such a missing tree as usual,
+                        * but will not attempt to recurse into this tree
+                        * object.
+                        */
+                       do_not_die_on_missing_tree:1,
+
                        /* for internal use only */
                        exclude_promisor_objects:1;
 
index 84b883c..d679cc2 100644 (file)
@@ -380,7 +380,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
        set_error_routine(old_errfn);
 }
 
-static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
+static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
 {
        if (!cmd->argv[0])
                BUG("command is empty");
@@ -403,16 +403,22 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
        /*
         * If there are no '/' characters in the command then perform a path
         * lookup and use the resolved path as the command to exec.  If there
-        * are no '/' characters or if the command wasn't found in the path,
-        * have exec attempt to invoke the command directly.
+        * are '/' characters, we have exec attempt to invoke the command
+        * directly.
         */
        if (!strchr(out->argv[1], '/')) {
                char *program = locate_in_PATH(out->argv[1]);
                if (program) {
                        free((char *)out->argv[1]);
                        out->argv[1] = program;
+               } else {
+                       argv_array_clear(out);
+                       errno = ENOENT;
+                       return -1;
                }
        }
+
+       return 0;
 }
 
 static char **prep_childenv(const char *const *deltaenv)
@@ -719,6 +725,12 @@ fail_pipe:
        struct child_err cerr;
        struct atfork_state as;
 
+       if (prepare_cmd(&argv, cmd) < 0) {
+               failed_errno = errno;
+               cmd->pid = -1;
+               goto end_of_spawn;
+       }
+
        if (pipe(notify_pipe))
                notify_pipe[0] = notify_pipe[1] = -1;
 
@@ -729,7 +741,6 @@ fail_pipe:
                set_cloexec(null_fd);
        }
 
-       prepare_cmd(&argv, cmd);
        childenv = prep_childenv(cmd->env);
        atfork_prepare(&as);
 
@@ -857,6 +868,8 @@ fail_pipe:
        argv_array_clear(&argv);
        free(childenv);
 }
+end_of_spawn:
+
 #else
 {
        int fhin = 0, fhout = 1, fherr = 2;
index 84f067e..5820412 100644 (file)
@@ -111,7 +111,7 @@ static void mark_entry_for_delete(size_t pos, void *data)
                die("position for delete %d exceeds base index size %d",
                    (int)pos, istate->cache_nr);
        istate->cache[pos]->ce_flags |= CE_REMOVE;
-       istate->split_index->nr_deletions = 1;
+       istate->split_index->nr_deletions++;
 }
 
 static void replace_entry(size_t pos, void *data)
@@ -188,6 +188,30 @@ void merge_base_index(struct index_state *istate)
        si->saved_cache_nr = 0;
 }
 
+/*
+ * Compare most of the fields in two cache entries, i.e. all except the
+ * hashmap_entry and the name.
+ */
+static int compare_ce_content(struct cache_entry *a, struct cache_entry *b)
+{
+       const unsigned int ondisk_flags = CE_STAGEMASK | CE_VALID |
+                                         CE_EXTENDED_FLAGS;
+       unsigned int ce_flags = a->ce_flags;
+       unsigned int base_flags = b->ce_flags;
+       int ret;
+
+       /* only on-disk flags matter */
+       a->ce_flags &= ondisk_flags;
+       b->ce_flags &= ondisk_flags;
+       ret = memcmp(&a->ce_stat_data, &b->ce_stat_data,
+                    offsetof(struct cache_entry, name) -
+                    offsetof(struct cache_entry, ce_stat_data));
+       a->ce_flags = ce_flags;
+       b->ce_flags = base_flags;
+
+       return ret;
+}
+
 void prepare_to_write_split_index(struct index_state *istate)
 {
        struct split_index *si = init_split_index(istate);
@@ -207,38 +231,109 @@ void prepare_to_write_split_index(struct index_state *istate)
                 */
                for (i = 0; i < istate->cache_nr; i++) {
                        struct cache_entry *base;
-                       /* namelen is checked separately */
-                       const unsigned int ondisk_flags =
-                               CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS;
-                       unsigned int ce_flags, base_flags, ret;
                        ce = istate->cache[i];
-                       if (!ce->index)
+                       if (!ce->index) {
+                               /*
+                                * During simple update index operations this
+                                * is a cache entry that is not present in
+                                * the shared index.  It will be added to the
+                                * split index.
+                                *
+                                * However, it might also represent a file
+                                * that already has a cache entry in the
+                                * shared index, but a new index has just
+                                * been constructed by unpack_trees(), and
+                                * this entry now refers to different content
+                                * than what was recorded in the original
+                                * index, e.g. during 'read-tree -m HEAD^' or
+                                * 'checkout HEAD^'.  In this case the
+                                * original entry in the shared index will be
+                                * marked as deleted, and this entry will be
+                                * added to the split index.
+                                */
                                continue;
+                       }
                        if (ce->index > si->base->cache_nr) {
-                               ce->index = 0;
-                               continue;
+                               BUG("ce refers to a shared ce at %d, which is beyond the shared index size %d",
+                                   ce->index, si->base->cache_nr);
                        }
                        ce->ce_flags |= CE_MATCHED; /* or "shared" */
                        base = si->base->cache[ce->index - 1];
-                       if (ce == base)
+                       if (ce == base) {
+                               /* The entry is present in the shared index. */
+                               if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+                                       /*
+                                        * Already marked for inclusion in
+                                        * the split index, either because
+                                        * the corresponding file was
+                                        * modified and the cached stat data
+                                        * was refreshed, or because there
+                                        * is already a replacement entry in
+                                        * the split index.
+                                        * Nothing more to do here.
+                                        */
+                               } else if (!ce_uptodate(ce) &&
+                                          is_racy_timestamp(istate, ce)) {
+                                       /*
+                                        * A racily clean cache entry stored
+                                        * only in the shared index: it must
+                                        * be added to the split index, so
+                                        * the subsequent do_write_index()
+                                        * can smudge its stat data.
+                                        */
+                                       ce->ce_flags |= CE_UPDATE_IN_BASE;
+                               } else {
+                                       /*
+                                        * The entry is only present in the
+                                        * shared index and it was not
+                                        * refreshed.
+                                        * Just leave it there.
+                                        */
+                               }
                                continue;
+                       }
                        if (ce->ce_namelen != base->ce_namelen ||
                            strcmp(ce->name, base->name)) {
                                ce->index = 0;
                                continue;
                        }
-                       ce_flags = ce->ce_flags;
-                       base_flags = base->ce_flags;
-                       /* only on-disk flags matter */
-                       ce->ce_flags   &= ondisk_flags;
-                       base->ce_flags &= ondisk_flags;
-                       ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data,
-                                    offsetof(struct cache_entry, name) -
-                                    offsetof(struct cache_entry, ce_stat_data));
-                       ce->ce_flags = ce_flags;
-                       base->ce_flags = base_flags;
-                       if (ret)
+                       /*
+                        * This is the copy of a cache entry that is present
+                        * in the shared index, created by unpack_trees()
+                        * while it constructed a new index.
+                        */
+                       if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+                               /*
+                                * Already marked for inclusion in the split
+                                * index, either because the corresponding
+                                * file was modified and the cached stat data
+                                * was refreshed, or because the original
+                                * entry already had a replacement entry in
+                                * the split index.
+                                * Nothing to do.
+                                */
+                       } else if (!ce_uptodate(ce) &&
+                                  is_racy_timestamp(istate, ce)) {
+                               /*
+                                * A copy of a racily clean cache entry from
+                                * the shared index.  It must be added to
+                                * the split index, so the subsequent
+                                * do_write_index() can smudge its stat data.
+                                */
                                ce->ce_flags |= CE_UPDATE_IN_BASE;
+                       } else {
+                               /*
+                                * Thoroughly compare the cached data to see
+                                * whether it should be marked for inclusion
+                                * in the split index.
+                                *
+                                * This comparison might be unnecessary, as
+                                * code paths modifying the cached data do
+                                * set CE_UPDATE_IN_BASE as well.
+                                */
+                               if (compare_ce_content(ce, base))
+                                       ce->ce_flags |= CE_UPDATE_IN_BASE;
+                       }
                        discard_cache_entry(base);
                        si->base->cache[ce->index - 1] = ce;
                }
index 60a35ae..bf18fdd 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -87,7 +87,7 @@ struct object_id;
  * Initialize the structure. The second parameter can be zero or a bigger
  * number to allocate memory, in case you want to prevent further reallocs.
  */
-extern void strbuf_init(struct strbuf *, size_t);
+void strbuf_init(struct strbuf *sb, size_t alloc);
 
 /**
  * Release a string buffer and the memory it used. After this call, the
@@ -97,7 +97,7 @@ extern void strbuf_init(struct strbuf *, size_t);
  * To clear a strbuf in preparation for further use without the overhead
  * of free()ing and malloc()ing again, use strbuf_reset() instead.
  */
-extern void strbuf_release(struct strbuf *);
+void strbuf_release(struct strbuf *sb);
 
 /**
  * Detach the string from the strbuf and returns it; you now own the
@@ -107,7 +107,7 @@ extern void strbuf_release(struct strbuf *);
  * The strbuf that previously held the string is reset to `STRBUF_INIT` so
  * it can be reused after calling this function.
  */
-extern char *strbuf_detach(struct strbuf *, size_t *);
+char *strbuf_detach(struct strbuf *sb, size_t *sz);
 
 /**
  * Attach a string to a buffer. You should specify the string to attach,
@@ -117,7 +117,7 @@ extern char *strbuf_detach(struct strbuf *, size_t *);
  * malloc()ed, and after attaching, the pointer cannot be relied upon
  * anymore, and neither be free()d directly.
  */
-extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
+void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t mem);
 
 /**
  * Swap the contents of two string buffers.
@@ -148,7 +148,7 @@ static inline size_t strbuf_avail(const struct strbuf *sb)
  * This is never a needed operation, but can be critical for performance in
  * some cases.
  */
-extern void strbuf_grow(struct strbuf *, size_t);
+void strbuf_grow(struct strbuf *sb, size_t amount);
 
 /**
  * Set the length of the buffer to a given value. This function does *not*
@@ -183,30 +183,30 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len)
  * Strip whitespace from the beginning (`ltrim`), end (`rtrim`), or both side
  * (`trim`) of a string.
  */
-extern void strbuf_trim(struct strbuf *);
-extern void strbuf_rtrim(struct strbuf *);
-extern void strbuf_ltrim(struct strbuf *);
+void strbuf_trim(struct strbuf *sb);
+void strbuf_rtrim(struct strbuf *sb);
+void strbuf_ltrim(struct strbuf *sb);
 
 /* Strip trailing directory separators */
-extern void strbuf_trim_trailing_dir_sep(struct strbuf *);
+void strbuf_trim_trailing_dir_sep(struct strbuf *sb);
 
 /**
  * Replace the contents of the strbuf with a reencoded form.  Returns -1
  * on error, 0 on success.
  */
-extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
+int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
 
 /**
  * Lowercase each character in the buffer using `tolower`.
  */
-extern void strbuf_tolower(struct strbuf *sb);
+void strbuf_tolower(struct strbuf *sb);
 
 /**
  * Compare two buffers. Returns an integer less than, equal to, or greater
  * than zero if the first buffer is found, respectively, to be less than,
  * to match, or be greater than the second buffer.
  */
-extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
+int strbuf_cmp(const struct strbuf *first, const struct strbuf *second);
 
 
 /**
@@ -233,37 +233,38 @@ static inline void strbuf_addch(struct strbuf *sb, int c)
 /**
  * Add a character the specified number of times to the buffer.
  */
-extern void strbuf_addchars(struct strbuf *sb, int c, size_t n);
+void strbuf_addchars(struct strbuf *sb, int c, size_t n);
 
 /**
  * Insert data to the given position of the buffer. The remaining contents
  * will be shifted, not overwritten.
  */
-extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t);
+void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);
 
 /**
  * Remove given amount of data from a given position of the buffer.
  */
-extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
+void strbuf_remove(struct strbuf *sb, size_t pos, size_t len);
 
 /**
  * Remove the bytes between `pos..pos+len` and replace it with the given
  * data.
  */
-extern void strbuf_splice(struct strbuf *, size_t pos, size_t len,
-                         const void *, size_t);
+void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
+                  const void *data, size_t data_len);
 
 /**
  * Add a NUL-terminated string to the buffer. Each line will be prepended
  * by a comment character and a blank.
  */
-extern void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size);
+void strbuf_add_commented_lines(struct strbuf *out,
+                               const char *buf, size_t size);
 
 
 /**
  * Add data of given length to the buffer.
  */
-extern void strbuf_add(struct strbuf *, const void *, size_t);
+void strbuf_add(struct strbuf *sb, const void *data, size_t len);
 
 /**
  * Add a NUL-terminated string to the buffer.
@@ -282,7 +283,7 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)
 /**
  * Copy the contents of another buffer at the end of the current one.
  */
-extern void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
+void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
 
 /**
  * This function can be used to expand a format string containing
@@ -308,8 +309,13 @@ extern void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
  * parameters to the callback, `strbuf_expand()` passes a context pointer,
  * which can be used by the programmer of the callback as she sees fit.
  */
-typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
-extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
+typedef size_t (*expand_fn_t) (struct strbuf *sb,
+                              const char *placeholder,
+                              void *context);
+void strbuf_expand(struct strbuf *sb,
+                  const char *format,
+                  expand_fn_t fn,
+                  void *context);
 
 /**
  * Used as callback for `strbuf_expand()`, expects an array of
@@ -321,7 +327,9 @@ struct strbuf_expand_dict_entry {
        const char *placeholder;
        const char *value;
 };
-extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
+size_t strbuf_expand_dict_cb(struct strbuf *sb,
+                            const char *placeholder,
+                            void *context);
 
 /**
  * Append the contents of one strbuf to another, quoting any
@@ -329,29 +337,29 @@ extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
  * destination. This is useful for literal data to be fed to either
  * strbuf_expand or to the *printf family of functions.
  */
-extern void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
+void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
 
 /**
  * Append the given byte size as a human-readable string (i.e. 12.23 KiB,
  * 3.50 MiB).
  */
-extern void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
+void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
 
 /**
  * Add a formatted string to the buffer.
  */
 __attribute__((format (printf,2,3)))
-extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
+void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 
 /**
  * Add a formatted string prepended by a comment character and a
  * blank to the buffer.
  */
 __attribute__((format (printf, 2, 3)))
-extern void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
+void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
 
 __attribute__((format (printf,2,0)))
-extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
+void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
 
 /**
  * Add the time specified by `tm`, as formatted by `strftime`.
@@ -361,9 +369,9 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
  * `suppress_tz_name`, when set, expands %Z internally to the empty
  * string rather than passing it to `strftime`.
  */
-extern void strbuf_addftime(struct strbuf *sb, const char *fmt,
-                           const struct tm *tm, int tz_offset,
-                           int suppress_tz_name);
+void strbuf_addftime(struct strbuf *sb, const char *fmt,
+                   const struct tm *tm, int tz_offset,
+                   int suppress_tz_name);
 
 /**
  * Read a given size of data from a FILE* pointer to the buffer.
@@ -373,14 +381,14 @@ extern void strbuf_addftime(struct strbuf *sb, const char *fmt,
  * `strbuf_read()`, `strbuf_read_file()` and `strbuf_getline_*()`
  * family of functions have the same behaviour as well.
  */
-extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
+size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *file);
 
 /**
  * Read the contents of a given file descriptor. The third argument can be
  * used to give a hint about the file size, to avoid reallocs.  If read fails,
  * any partial read is undone.
  */
-extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
+ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint);
 
 /**
  * Read the contents of a given file descriptor partially by using only one
@@ -388,7 +396,7 @@ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
  * file size, to avoid reallocs. Returns the number of new bytes appended to
  * the sb.
  */
-extern ssize_t strbuf_read_once(struct strbuf *, int fd, size_t hint);
+ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint);
 
 /**
  * Read the contents of a file, specified by its path. The third argument
@@ -396,19 +404,19 @@ extern ssize_t strbuf_read_once(struct strbuf *, int fd, size_t hint);
  * Return the number of bytes read or a negative value if some error
  * occurred while opening or reading the file.
  */
-extern ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
+ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 
 /**
  * Read the target of a symbolic link, specified by its path.  The third
  * argument can be used to give a hint about the size, to avoid reallocs.
  */
-extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
+int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 
 /**
  * Write the whole content of the strbuf to the stream not stopping at
  * NUL bytes.
  */
-extern ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
+ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
 
 /**
  * Read a line from a FILE *, overwriting the existing contents of
@@ -422,10 +430,10 @@ extern ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
 typedef int (*strbuf_getline_fn)(struct strbuf *, FILE *);
 
 /* Uses LF as the line terminator */
-extern int strbuf_getline_lf(struct strbuf *sb, FILE *fp);
+int strbuf_getline_lf(struct strbuf *sb, FILE *fp);
 
 /* Uses NUL as the line terminator */
-extern int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
+int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
 
 /*
  * Similar to strbuf_getline_lf(), but additionally treats a CR that
@@ -434,14 +442,14 @@ extern int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
  * that can come from platforms whose native text format is CRLF
  * terminated.
  */
-extern int strbuf_getline(struct strbuf *, FILE *);
+int strbuf_getline(struct strbuf *sb, FILE *file);
 
 
 /**
  * Like `strbuf_getline`, but keeps the trailing terminator (if
  * any) in the buffer.
  */
-extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
+int strbuf_getwholeline(struct strbuf *sb, FILE *file, int term);
 
 /**
  * Like `strbuf_getwholeline`, but operates on a file descriptor.
@@ -449,19 +457,19 @@ extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
  * use it unless you need the correct position in the file
  * descriptor.
  */
-extern int strbuf_getwholeline_fd(struct strbuf *, int, int);
+int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term);
 
 /**
  * Set the buffer to the path of the current working directory.
  */
-extern int strbuf_getcwd(struct strbuf *sb);
+int strbuf_getcwd(struct strbuf *sb);
 
 /**
  * Add a path to a buffer, converting a relative path to an
  * absolute one in the process.  Symbolic links are not
  * resolved.
  */
-extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
 
 /**
  * Canonize `path` (make it absolute, resolve symlinks, remove extra
@@ -475,7 +483,7 @@ extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
  * Callers that don't mind links should use the more lightweight
  * strbuf_add_absolute_path() instead.
  */
-extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
+void strbuf_add_real_path(struct strbuf *sb, const char *path);
 
 
 /**
@@ -483,13 +491,13 @@ extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
  * normalize_path_copy() for details. If an error occurs, the contents of "sb"
  * are left untouched, and -1 is returned.
  */
-extern int strbuf_normalize_path(struct strbuf *sb);
+int strbuf_normalize_path(struct strbuf *sb);
 
 /**
  * Strip whitespace from a buffer. The second parameter controls if
  * comments are considered contents to be removed or not.
  */
-extern void strbuf_stripspace(struct strbuf *buf, int skip_comments);
+void strbuf_stripspace(struct strbuf *buf, int skip_comments);
 
 static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
 {
@@ -518,8 +526,8 @@ static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
  * For lighter-weight alternatives, see string_list_split() and
  * string_list_split_in_place().
  */
-extern struct strbuf **strbuf_split_buf(const char *, size_t,
-                                       int terminator, int max);
+struct strbuf **strbuf_split_buf(const char *str, size_t len,
+                                int terminator, int max);
 
 static inline struct strbuf **strbuf_split_str(const char *str,
                                               int terminator, int max)
@@ -528,7 +536,7 @@ static inline struct strbuf **strbuf_split_str(const char *str,
 }
 
 static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
-                                               int terminator, int max)
+                                              int terminator, int max)
 {
        return strbuf_split_buf(sb->buf, sb->len, terminator, max);
 }
@@ -549,23 +557,23 @@ static inline struct strbuf **strbuf_split(const struct strbuf *sb,
  *   'element1, element2, ..., elementN'
  * to str.  If only one element, just write "element1" to str.
  */
-extern void strbuf_add_separated_string_list(struct strbuf *str,
-                                            const char *sep,
-                                            struct string_list *slist);
+void strbuf_add_separated_string_list(struct strbuf *str,
+                                     const char *sep,
+                                     struct string_list *slist);
 
 /**
  * Free a NULL-terminated list of strbufs (for example, the return
  * values of the strbuf_split*() functions).
  */
-extern void strbuf_list_free(struct strbuf **);
+void strbuf_list_free(struct strbuf **list);
 
 /**
  * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
  * the strbuf `sb`.
  */
-extern void strbuf_add_unique_abbrev(struct strbuf *sb,
-                                    const struct object_id *oid,
-                                    int abbrev_len);
+void strbuf_add_unique_abbrev(struct strbuf *sb,
+                             const struct object_id *oid,
+                             int abbrev_len);
 
 /**
  * Launch the user preferred editor to edit a file and fill the buffer
@@ -574,15 +582,21 @@ extern void strbuf_add_unique_abbrev(struct strbuf *sb,
  * run in. If the buffer is NULL the editor is launched as usual but the
  * file's contents are not read into the buffer upon completion.
  */
-extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
+int launch_editor(const char *path,
+                 struct strbuf *buffer,
+                 const char *const *env);
 
-extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
+void strbuf_add_lines(struct strbuf *sb,
+                     const char *prefix,
+                     const char *buf,
+                     size_t size);
 
 /**
  * Append s to sb, with the characters '<', '>', '&' and '"' converted
  * into XML entities.
  */
-extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s);
+void strbuf_addstr_xml_quoted(struct strbuf *sb,
+                             const char *s);
 
 /**
  * "Complete" the contents of `sb` by ensuring that either it ends with the
@@ -612,8 +626,8 @@ static inline void strbuf_complete_line(struct strbuf *sb)
  * 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);
+void strbuf_branchname(struct strbuf *sb, const char *name,
+                      unsigned allowed);
 
 /*
  * Like strbuf_branchname() above, but confirm that the result is
@@ -621,15 +635,15 @@ extern void strbuf_branchname(struct strbuf *sb, const char *name,
  *
  * 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);
+int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
-extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
-                                   int reserved);
+void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
+                            int reserved);
 
 __attribute__((format (printf,1,2)))
-extern int printf_ln(const char *fmt, ...);
+int printf_ln(const char *fmt, ...);
 __attribute__((format (printf,2,3)))
-extern int fprintf_ln(FILE *fp, const char *fmt, ...);
+int fprintf_ln(FILE *fp, const char *fmt, ...);
 
 char *xstrdup_tolower(const char *);
 char *xstrdup_toupper(const char *);
index 6b5836d..5df8b68 100644 (file)
@@ -55,13 +55,23 @@ static struct test_cmd cmds[] = {
        { "write-cache", cmd__write_cache },
 };
 
+static NORETURN void die_usage(void)
+{
+       size_t i;
+
+       fprintf(stderr, "usage: test-tool <toolname> [args]\n");
+       for (i = 0; i < ARRAY_SIZE(cmds); i++)
+               fprintf(stderr, "  %s\n", cmds[i].name);
+       exit(128);
+}
+
 int cmd_main(int argc, const char **argv)
 {
        int i;
 
        BUG_exit_code = 99;
        if (argc < 2)
-               die("I need a test name!");
+               die_usage();
 
        for (i = 0; i < ARRAY_SIZE(cmds); i++) {
                if (!strcmp(cmds[i].name, argv[1])) {
@@ -70,5 +80,6 @@ int cmd_main(int argc, const char **argv)
                        return cmds[i].fn(argc, argv);
                }
        }
-       die("There is no test named '%s'", argv[1]);
+       error("there is no tool named '%s'", argv[1]);
+       die_usage();
 }
index e489056..71f470b 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __TEST_TOOL_H__
-#define __TEST_TOOL_H__
+#ifndef TEST_TOOL_H
+#define TEST_TOOL_H
 
 #include "git-compat-util.h"
 
index 3e131c5..cf932c8 100755 (executable)
@@ -12,10 +12,14 @@ cat >hello-script <<-EOF
        cat hello-script
 EOF
 
-test_expect_success 'start_command reports ENOENT' '
+test_expect_success 'start_command reports ENOENT (slash)' '
        test-tool run-command start-command-ENOENT ./does-not-exist
 '
 
+test_expect_success 'start_command reports ENOENT (no slash)' '
+       test-tool run-command start-command-ENOENT does-not-exist
+'
+
 test_expect_success 'run_command can run a command' '
        cat hello-script >hello.sh &&
        chmod +x hello.sh &&
@@ -25,6 +29,13 @@ test_expect_success 'run_command can run a command' '
        test_must_be_empty err
 '
 
+test_expect_success 'run_command is restricted to PATH' '
+       write_script should-not-run <<-\EOF &&
+       echo yikes
+       EOF
+       test_must_fail test-tool run-command run-command should-not-run
+'
+
 test_expect_success !MINGW 'run_command can run a script without a #! line' '
        cat >hello <<-\EOF &&
        cat hello-script
index c521d7d..ba3887f 100755 (executable)
@@ -239,6 +239,51 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' '
        ! grep $FOO out
 '
 
+test_expect_success 'missing tree objects with --missing=allow-promisor and --exclude-promisor-objects' '
+       rm -rf repo &&
+       test_create_repo repo &&
+       test_commit -C repo foo &&
+       test_commit -C repo bar &&
+       test_commit -C repo baz &&
+
+       promise_and_delete $(git -C repo rev-parse bar^{tree}) &&
+       promise_and_delete $(git -C repo rev-parse foo^{tree}) &&
+
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "arbitrary string" &&
+
+       git -C repo rev-list --missing=allow-promisor --objects HEAD >objs 2>rev_list_err &&
+       test_must_be_empty rev_list_err &&
+       # 3 commits, 3 blobs, and 1 tree
+       test_line_count = 7 objs &&
+
+       # Do the same for --exclude-promisor-objects, but with all trees gone.
+       promise_and_delete $(git -C repo rev-parse baz^{tree}) &&
+       git -C repo rev-list --exclude-promisor-objects --objects HEAD >objs 2>rev_list_err &&
+       test_must_be_empty rev_list_err &&
+       # 3 commits, no blobs or trees
+       test_line_count = 3 objs
+'
+
+test_expect_success 'missing non-root tree object and rev-list' '
+       rm -rf repo &&
+       test_create_repo repo &&
+       mkdir repo/dir &&
+       echo foo >repo/dir/foo &&
+       git -C repo add dir/foo &&
+       git -C repo commit -m "commit dir/foo" &&
+
+       promise_and_delete $(git -C repo rev-parse HEAD:dir) &&
+
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "arbitrary string" &&
+
+       git -C repo rev-list --missing=allow-any --objects HEAD >objs 2>rev_list_err &&
+       test_must_be_empty rev_list_err &&
+       # 1 commit and 1 tree
+       test_line_count = 2 objs
+'
+
 test_expect_success 'rev-list stops traversal at missing and promised tree' '
        rm -rf repo &&
        test_create_repo repo &&
index 3e618ca..2ac47aa 100755 (executable)
@@ -13,6 +13,13 @@ sane_unset GIT_TEST_SPLIT_INDEX
 sane_unset GIT_TEST_FSMONITOR
 sane_unset GIT_TEST_INDEX_THREADS
 
+# Create a file named as $1 with content read from stdin.
+# Set the file's mtime to a few seconds in the past to avoid racy situations.
+create_non_racy_file () {
+       cat >"$1" &&
+       test-tool chmtime =-5 "$1"
+}
+
 test_expect_success 'enable split index' '
        git config splitIndex.maxPercentChange 100 &&
        git update-index --split-index &&
@@ -36,7 +43,7 @@ test_expect_success 'enable split index' '
 '
 
 test_expect_success 'add one file' '
-       : >one &&
+       create_non_racy_file one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -88,7 +95,7 @@ test_expect_success 'enable split index again, "one" now belongs to base index"'
 '
 
 test_expect_success 'modify original file, base index untouched' '
-       echo modified >one &&
+       echo modified | create_non_racy_file one &&
        git update-index one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -107,7 +114,7 @@ test_expect_success 'modify original file, base index untouched' '
 '
 
 test_expect_success 'add another file, which stays index' '
-       : >two &&
+       create_non_racy_file two &&
        git update-index --add two &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -160,7 +167,7 @@ test_expect_success 'remove file in base index' '
 '
 
 test_expect_success 'add original file back' '
-       : >one &&
+       create_non_racy_file one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -179,7 +186,7 @@ test_expect_success 'add original file back' '
 '
 
 test_expect_success 'add new file' '
-       : >two &&
+       create_non_racy_file two &&
        git update-index --add two &&
        git ls-files --stage >actual &&
        cat >expect <<-EOF &&
@@ -223,7 +230,7 @@ test_expect_success 'rev-parse --shared-index-path' '
 
 test_expect_success 'set core.splitIndex config variable to true' '
        git config core.splitIndex true &&
-       : >three &&
+       create_non_racy_file three &&
        git update-index --add three &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -258,9 +265,9 @@ test_expect_success 'set core.splitIndex config variable to false' '
        test_cmp expect actual
 '
 
-test_expect_success 'set core.splitIndex config variable to true' '
+test_expect_success 'set core.splitIndex config variable back to true' '
        git config core.splitIndex true &&
-       : >three &&
+       create_non_racy_file three &&
        git update-index --add three &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -270,7 +277,7 @@ test_expect_success 'set core.splitIndex config variable to true' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >four &&
+       create_non_racy_file four &&
        git update-index --add four &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
        cat >expect <<-EOF &&
@@ -284,7 +291,7 @@ test_expect_success 'set core.splitIndex config variable to true' '
 
 test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
        git config --unset splitIndex.maxPercentChange &&
-       : >five &&
+       create_non_racy_file five &&
        git update-index --add five &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -294,7 +301,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >six &&
+       create_non_racy_file six &&
        git update-index --add six &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
        cat >expect <<-EOF &&
@@ -308,7 +315,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
 
 test_expect_success 'check splitIndex.maxPercentChange set to 0' '
        git config splitIndex.maxPercentChange 0 &&
-       : >seven &&
+       create_non_racy_file seven &&
        git update-index --add seven &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -318,7 +325,7 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >eight &&
+       create_non_racy_file eight &&
        git update-index --add eight &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -331,17 +338,17 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' '
 '
 
 test_expect_success 'shared index files expire after 2 weeks by default' '
-       : >ten &&
+       create_non_racy_file ten &&
        git update-index --add ten &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_under_2_weeks_ago=$((5-14*86400)) &&
        test-tool chmtime =$just_under_2_weeks_ago .git/sharedindex.* &&
-       : >eleven &&
+       create_non_racy_file eleven &&
        git update-index --add eleven &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_over_2_weeks_ago=$((-1-14*86400)) &&
        test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
-       : >twelve &&
+       create_non_racy_file twelve &&
        git update-index --add twelve &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -349,12 +356,12 @@ test_expect_success 'shared index files expire after 2 weeks by default' '
 test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' '
        git config splitIndex.sharedIndexExpire "16.days.ago" &&
        test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
-       : >thirteen &&
+       create_non_racy_file thirteen &&
        git update-index --add thirteen &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_over_16_days_ago=$((-1-16*86400)) &&
        test-tool chmtime =$just_over_16_days_ago .git/sharedindex.* &&
-       : >fourteen &&
+       create_non_racy_file fourteen &&
        git update-index --add fourteen &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -363,13 +370,13 @@ test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"
        git config splitIndex.sharedIndexExpire never &&
        just_10_years_ago=$((-365*10*86400)) &&
        test-tool chmtime =$just_10_years_ago .git/sharedindex.* &&
-       : >fifteen &&
+       create_non_racy_file 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-tool chmtime =$just_1_second_ago .git/sharedindex.* &&
-       : >sixteen &&
+       create_non_racy_file sixteen &&
        git update-index --add sixteen &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -384,7 +391,7 @@ do
                # Create one new shared index file
                git config core.sharedrepository "$mode" &&
                git config core.splitIndex true &&
-               : >one &&
+               create_non_racy_file one &&
                git update-index --add one &&
                echo "$modebits" >expect &&
                test_modebits .git/index >actual &&
diff --git a/t/t1701-racy-split-index.sh b/t/t1701-racy-split-index.sh
new file mode 100755 (executable)
index 0000000..5dc221e
--- /dev/null
@@ -0,0 +1,214 @@
+#!/bin/sh
+
+# This test can give false success if your machine is sufficiently
+# slow or all trials happened to happen on second boundaries.
+
+test_description='racy split index'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       # Only split the index when the test explicitly says so.
+       sane_unset GIT_TEST_SPLIT_INDEX &&
+       git config splitIndex.maxPercentChange 100 &&
+
+       echo "cached content" >racy-file &&
+       git add racy-file &&
+       git commit -m initial &&
+
+       echo something >other-file &&
+       # No raciness with this file.
+       test-tool chmtime =-20 other-file &&
+
+       echo "+cached content" >expect
+'
+
+check_cached_diff () {
+       git diff-index --patch --cached $EMPTY_TREE racy-file >diff &&
+       tail -1 diff >actual &&
+       test_cmp expect actual
+}
+
+trials="0 1 2 3 4"
+for trial in $trials
+do
+       test_expect_success "split the index while adding a racily clean file #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second (so both writes to racy-file result in the same
+               # mtime) to create the interesting racy situation.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Subsequent git commands should notice that racy-file
+               # and the split index have the same mtime, and check
+               # the content of the file to see if it is actually
+               # clean.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "add a racily clean file to an already split index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               git update-index --split-index &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update the split index.  The cache entry of racy-file
+               # will be stored only in the split index.
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Subsequent git commands should notice that racy-file
+               # and the split index have the same mtime, and check
+               # the content of the file to see if it is actually
+               # clean.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "split the index when the index contains a racily clean cache entry #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update and split the index when the index contains
+               # the racily clean cache entry of racy-file.
+               # A corresponding replacement cache entry with smudged
+               # stat data should be added to the new split index.
+               git update-index --split-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data in the replacement cache entry and that it
+               # doesnt match with the file the worktree.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index when it contains a new racily clean cache entry #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               git update-index --split-index &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update the split index.  The cache entry of racy-file
+               # will be stored only in the split index.
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index when the racily clean cache
+               # entry of racy-file is only stored in the split index.
+               # An updated cache entry with smudged stat data should
+               # be added to the new split index.
+               git update-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index when a racily clean cache entry is stored only in the shared index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index when the racily clean cache
+               # entry of racy-file is only stored in the shared index.
+               # A corresponding replacement cache entry with smudged
+               # stat data should be added to the new split index.
+               git update-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index after unpack trees() copied a racily clean cache entry from the shared index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index after unpack_trees() copied the
+               # racily clean cache entry of racy-file from the shared
+               # index.  A corresponding replacement cache entry
+               # with smudged stat data should be added to the new
+               # split index.
+               git read-tree -m HEAD &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+test_done
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
deleted file mode 100755 (executable)
index de3edb5..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-test_description='git checkout-index test.
-
-This test registers the following filesystem structure in the
-cache:
-
-    path0       - a file
-    path1/file1 - a file in a directory
-
-And then tries to checkout in a work tree that has the following:
-
-    path0/file0 - a file in a directory
-    path1       - a file
-
-The git checkout-index command should fail when attempting to checkout
-path0, finding it is occupied by a directory, and path1/file1, finding
-path1 is occupied by a non-directory.  With "-f" flag, it should remove
-the conflicting paths and succeed.
-'
-. ./test-lib.sh
-
-date >path0
-mkdir path1
-date >path1/file1
-
-test_expect_success \
-    'git update-index --add various paths.' \
-    'git update-index --add path0 path1/file1'
-
-rm -fr path0 path1
-mkdir path0
-date >path0/file0
-date >path1
-
-test_expect_success \
-    'git checkout-index without -f should fail on conflicting work tree.' \
-    'test_must_fail git checkout-index -a'
-
-test_expect_success \
-    'git checkout-index with -f should succeed.' \
-    'git checkout-index -f -a'
-
-test_expect_success \
-    'git checkout-index conflicting paths.' \
-    'test -f path0 && test -d path1 && test -f path1/file1'
-
-test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
-       mkdir -p tar/get &&
-       ln -s tar/get there &&
-       echo first &&
-       git checkout-index -a -f --prefix=there/ &&
-       echo second &&
-       git checkout-index -a -f --prefix=there/
-'
-
-test_done
diff --git a/t/t2000-conflict-when-checking-files-out.sh b/t/t2000-conflict-when-checking-files-out.sh
new file mode 100755 (executable)
index 0000000..f18616a
--- /dev/null
@@ -0,0 +1,135 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git conflicts when checking files out test.'
+
+# The first test registers the following filesystem structure in the
+# cache:
+#
+#     path0       - a file
+#     path1/file1 - a file in a directory
+#
+# And then tries to checkout in a work tree that has the following:
+#
+#     path0/file0 - a file in a directory
+#     path1       - a file
+#
+# The git checkout-index command should fail when attempting to checkout
+# path0, finding it is occupied by a directory, and path1/file1, finding
+# path1 is occupied by a non-directory.  With "-f" flag, it should remove
+# the conflicting paths and succeed.
+
+. ./test-lib.sh
+
+show_files() {
+       # show filesystem files, just [-dl] for type and name
+       find path? -ls |
+       sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /'
+       # what's in the cache, just mode and name
+       git ls-files --stage |
+       sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /'
+       # what's in the tree, just mode and name.
+       git ls-tree -r "$1" |
+       sed -e 's/^\([0-9]*\)   [^ ]*   [0-9a-f]*       /tr: \1 /'
+}
+
+date >path0
+mkdir path1
+date >path1/file1
+
+test_expect_success \
+    'git update-index --add various paths.' \
+    'git update-index --add path0 path1/file1'
+
+rm -fr path0 path1
+mkdir path0
+date >path0/file0
+date >path1
+
+test_expect_success \
+    'git checkout-index without -f should fail on conflicting work tree.' \
+    'test_must_fail git checkout-index -a'
+
+test_expect_success \
+    'git checkout-index with -f should succeed.' \
+    'git checkout-index -f -a'
+
+test_expect_success \
+    'git checkout-index conflicting paths.' \
+    'test -f path0 && test -d path1 && test -f path1/file1'
+
+test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
+       mkdir -p tar/get &&
+       ln -s tar/get there &&
+       echo first &&
+       git checkout-index -a -f --prefix=there/ &&
+       echo second &&
+       git checkout-index -a -f --prefix=there/
+'
+
+# The second test registers the following filesystem structure in the cache:
+#
+#     path2/file0      - a file in a directory
+#     path3/file1 - a file in a directory
+#
+# and attempts to check it out when the work tree has:
+#
+#     path2/file0 - a file in a directory
+#     path3       - a symlink pointing at "path2"
+#
+# Checkout cache should fail to extract path3/file1 because the leading
+# path path3 is occupied by a non-directory.  With "-f" it should remove
+# the symlink path3 and create directory path3 and file path3/file1.
+
+mkdir path2
+date >path2/file0
+test_expect_success \
+    'git update-index --add path2/file0' \
+    'git update-index --add path2/file0'
+test_expect_success \
+    'writing tree out with git write-tree' \
+    'tree1=$(git write-tree)'
+test_debug 'show_files $tree1'
+
+mkdir path3
+date >path3/file1
+test_expect_success \
+    'git update-index --add path3/file1' \
+    'git update-index --add path3/file1'
+test_expect_success \
+    'writing tree out with git write-tree' \
+    'tree2=$(git write-tree)'
+test_debug 'show_files $tree2'
+
+rm -fr path3
+test_expect_success \
+    'read previously written tree and checkout.' \
+    'git read-tree -m $tree1 && git checkout-index -f -a'
+test_debug 'show_files $tree1'
+
+test_expect_success \
+    'add a symlink' \
+    'test_ln_s_add path2 path3'
+test_expect_success \
+    'writing tree out with git write-tree' \
+    'tree3=$(git write-tree)'
+test_debug 'show_files $tree3'
+
+# Morten says "Got that?" here.
+# Test begins.
+
+test_expect_success \
+    'read previously written tree and checkout.' \
+    'git read-tree $tree2 && git checkout-index -f -a'
+test_debug 'show_files $tree2'
+
+test_expect_success \
+    'checking out conflicting path with -f' \
+    'test ! -h path2 && test -d path2 &&
+     test ! -h path3 && test -d path3 &&
+     test ! -h path2/file0 && test -f path2/file0 &&
+     test ! -h path3/file1 && test -f path3/file1'
+
+test_done
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
deleted file mode 100755 (executable)
index 1fc8e63..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-test_description='git checkout-index test.
-
-This test registers the following filesystem structure in the cache:
-
-    path0/file0        - a file in a directory
-    path1/file1 - a file in a directory
-
-and attempts to check it out when the work tree has:
-
-    path0/file0 - a file in a directory
-    path1       - a symlink pointing at "path0"
-
-Checkout cache should fail to extract path1/file1 because the leading
-path path1 is occupied by a non-directory.  With "-f" it should remove
-the symlink path1 and create directory path1 and file path1/file1.
-'
-. ./test-lib.sh
-
-show_files() {
-       # show filesystem files, just [-dl] for type and name
-       find path? -ls |
-       sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /'
-       # what's in the cache, just mode and name
-       git ls-files --stage |
-       sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /'
-       # what's in the tree, just mode and name.
-       git ls-tree -r "$1" |
-       sed -e 's/^\([0-9]*\)   [^ ]*   [0-9a-f]*       /tr: \1 /'
-}
-
-mkdir path0
-date >path0/file0
-test_expect_success \
-    'git update-index --add path0/file0' \
-    'git update-index --add path0/file0'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree1=$(git write-tree)'
-test_debug 'show_files $tree1'
-
-mkdir path1
-date >path1/file1
-test_expect_success \
-    'git update-index --add path1/file1' \
-    'git update-index --add path1/file1'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree2=$(git write-tree)'
-test_debug 'show_files $tree2'
-
-rm -fr path1
-test_expect_success \
-    'read previously written tree and checkout.' \
-    'git read-tree -m $tree1 && git checkout-index -f -a'
-test_debug 'show_files $tree1'
-
-test_expect_success \
-    'add a symlink' \
-    'test_ln_s_add path0 path1'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree3=$(git write-tree)'
-test_debug 'show_files $tree3'
-
-# Morten says "Got that?" here.
-# Test begins.
-
-test_expect_success \
-    'read previously written tree and checkout.' \
-    'git read-tree $tree2 && git checkout-index -f -a'
-test_debug 'show_files $tree2'
-
-test_expect_success \
-    'checking out conflicting path with -f' \
-    'test ! -h path0 && test -d path0 &&
-     test ! -h path1 && test -d path1 &&
-     test ! -h path0/file0 && test -f path0/file0 &&
-     test ! -h path1/file1 && test -f path1/file1'
-
-test_done
index 045aca1..6aae364 100755 (executable)
@@ -122,6 +122,35 @@ test_expect_success 'changed commit' '
        test_cmp expected actual
 '
 
+test_expect_success 'changed commit with sm config' '
+       git range-diff --no-color --submodule=log topic...changed >actual &&
+       cat >expected <<-EOF &&
+       1:  4de457d = 1:  a4b3333 s/5/A/
+       2:  fccce22 = 2:  f51d370 s/4/A/
+       3:  147e64e ! 3:  0559556 s/11/B/
+           @@ -10,7 +10,7 @@
+             9
+             10
+            -11
+           -+B
+           ++BB
+             12
+             13
+             14
+       4:  a63e992 ! 4:  d966c5c s/12/B/
+           @@ -8,7 +8,7 @@
+            @@
+             9
+             10
+           - B
+           + BB
+            -12
+            +B
+             13
+       EOF
+       test_cmp expected actual
+'
+
 test_expect_success 'no commits on one side' '
        git commit --amend -m "new message" &&
        git range-diff master HEAD@{1} HEAD
index 453e6c3..6e0dd6f 100755 (executable)
@@ -127,4 +127,14 @@ test_expect_success 'diff --no-index from repo subdir respects config (implicit)
        test_cmp expect actual.head
 '
 
+test_expect_success 'diff --no-index from repo subdir with absolute paths' '
+       cat <<-EOF >expect &&
+       1       1       $(pwd)/non/git/{a => b}
+       EOF
+       test_expect_code 1 \
+               git -C repo/sub diff --numstat \
+               "$(pwd)/non/git/a" "$(pwd)/non/git/b" >actual &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh
new file mode 100755 (executable)
index 0000000..dab96c8
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='git log --graph of skewed left octopus merge.'
+
+. ./test-lib.sh
+
+test_expect_success 'set up merge history' '
+       cat >expect.uncolored <<-\EOF &&
+       * left
+       | *---.   octopus-merge
+       | |\ \ \
+       |/ / / /
+       | | | * 4
+       | | * | 3
+       | | |/
+       | * | 2
+       | |/
+       * | 1
+       |/
+       * initial
+       EOF
+       cat >expect.colors <<-\EOF &&
+       * left
+       <RED>|<RESET> *<BLUE>-<RESET><BLUE>-<RESET><MAGENTA>-<RESET><MAGENTA>.<RESET>   octopus-merge
+       <RED>|<RESET> <RED>|<RESET><YELLOW>\<RESET> <BLUE>\<RESET> <MAGENTA>\<RESET>
+       <RED>|<RESET><RED>/<RESET> <YELLOW>/<RESET> <BLUE>/<RESET> <MAGENTA>/<RESET>
+       <RED>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET> * 4
+       <RED>|<RESET> <YELLOW>|<RESET> * <MAGENTA>|<RESET> 3
+       <RED>|<RESET> <YELLOW>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       <RED>|<RESET> * <MAGENTA>|<RESET> 2
+       <RED>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       * <MAGENTA>|<RESET> 1
+       <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       * initial
+       EOF
+       test_commit initial &&
+       for i in 1 2 3 4 ; do
+               git checkout master -b $i || return $?
+               # Make tag name different from branch name, to avoid
+               # ambiguity error when calling checkout.
+               test_commit $i $i $i tag$i || return $?
+       done &&
+       git checkout 1 -b merge &&
+       test_tick &&
+       git merge -m octopus-merge 1 2 3 4 &&
+       git checkout 1 -b L &&
+       test_commit left
+'
+
+test_expect_success 'log --graph with tricky octopus merge with colors' '
+       test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
+       git log --color=always --graph --date-order --pretty=tformat:%s --all >actual.colors.raw &&
+       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
+       test_cmp expect.colors actual.colors
+'
+
+test_expect_success 'log --graph with tricky octopus merge, no color' '
+       git log --color=never --graph --date-order --pretty=tformat:%s --all >actual.raw &&
+       sed "s/ *\$//" actual.raw >actual &&
+       test_cmp expect.uncolored actual
+'
+
+# Repeat the previous two tests with "normal" octopus merge (i.e.,
+# without the first parent skewing to the "left" branch column).
+
+test_expect_success 'log --graph with normal octopus merge, no color' '
+       cat >expect.uncolored <<-\EOF &&
+       *---.   octopus-merge
+       |\ \ \
+       | | | * 4
+       | | * | 3
+       | | |/
+       | * | 2
+       | |/
+       * | 1
+       |/
+       * initial
+       EOF
+       git log --color=never --graph --date-order --pretty=tformat:%s merge >actual.raw &&
+       sed "s/ *\$//" actual.raw >actual &&
+       test_cmp expect.uncolored actual
+'
+
+test_expect_success 'log --graph with normal octopus merge with colors' '
+       cat >expect.colors <<-\EOF &&
+       *<YELLOW>-<RESET><YELLOW>-<RESET><BLUE>-<RESET><BLUE>.<RESET>   octopus-merge
+       <RED>|<RESET><GREEN>\<RESET> <YELLOW>\<RESET> <BLUE>\<RESET>
+       <RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET> * 4
+       <RED>|<RESET> <GREEN>|<RESET> * <BLUE>|<RESET> 3
+       <RED>|<RESET> <GREEN>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
+       <RED>|<RESET> * <BLUE>|<RESET> 2
+       <RED>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
+       * <BLUE>|<RESET> 1
+       <BLUE>|<RESET><BLUE>/<RESET>
+       * initial
+       EOF
+       test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
+       git log --color=always --graph --date-order --pretty=tformat:%s merge >actual.colors.raw &&
+       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
+       test_cmp expect.colors actual.colors
+'
+test_done
index 2e718f0..24541ea 100755 (executable)
@@ -67,6 +67,47 @@ test_expect_success 'verify normal and blob:none packfiles have same commits/tre
        test_cmp expected observed
 '
 
+test_expect_success 'get an error for missing tree object' '
+       git init r5 &&
+       echo foo >r5/foo &&
+       git -C r5 add foo &&
+       git -C r5 commit -m "foo" &&
+       del=$(git -C r5 rev-parse HEAD^{tree} | sed "s|..|&/|") &&
+       rm r5/.git/objects/$del &&
+       test_must_fail git -C r5 pack-objects --rev --stdout 2>bad_tree <<-EOF &&
+       HEAD
+       EOF
+       grep "bad tree object" bad_tree
+'
+
+test_expect_success 'setup for tests of tree:0' '
+       mkdir r1/subtree &&
+       echo "This is a file in a subtree" >r1/subtree/file &&
+       git -C r1 add subtree/file &&
+       git -C r1 commit -m subtree
+'
+
+test_expect_success 'verify tree:0 packfile has no blobs or trees' '
+       git -C r1 pack-objects --rev --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
+       HEAD
+       EOF
+       git -C r1 index-pack ../commitsonly.pack &&
+       git -C r1 verify-pack -v ../commitsonly.pack >objs &&
+       ! grep -E "tree|blob" objs
+'
+
+test_expect_success 'grab tree directly when using tree:0' '
+       # We should get the tree specified directly but not its blobs or subtrees.
+       git -C r1 pack-objects --rev --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
+       HEAD:
+       EOF
+       git -C r1 index-pack ../commitsonly.pack &&
+       git -C r1 verify-pack -v ../commitsonly.pack >objs &&
+       awk "/tree|blob/{print \$1}" objs >trees_and_blobs &&
+       git -C r1 rev-parse HEAD: >expected &&
+       test_cmp expected trees_and_blobs
+'
+
 # Test blob:limit=<n>[kmg] filter.
 # We boundary test around the size parameter.  The filter is strictly less than
 # the value, so size 500 and 1000 should have the same results, but 1001 should
diff --git a/t/t5321-pack-large-objects.sh b/t/t5321-pack-large-objects.sh
new file mode 100755 (executable)
index 0000000..a75eab8
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Copyright (c) 2018 Johannes Schindelin
+#
+
+test_description='git pack-object with "large" deltas
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pack.sh
+
+# Two similar-ish objects that we have computed deltas between.
+A=01d7713666f4de822776c7622c10f1b07de280dc
+B=e68fe8129b546b101aee9510c5328e7f21ca1d18
+
+test_expect_success 'setup' '
+       clear_packs &&
+       {
+               pack_header 2 &&
+               pack_obj $A $B &&
+               pack_obj $B
+       } >ab.pack &&
+       pack_trailer ab.pack &&
+       git index-pack --stdin <ab.pack
+'
+
+test_expect_success 'repack large deltas' '
+       printf "%s\\n" $A $B |
+       GIT_TEST_OE_DELTA_SIZE=2 git pack-objects tmp-pack
+'
+
+test_done
index 457c20c..f00d0da 100755 (executable)
@@ -23,7 +23,7 @@ test_expect_success 'with core.alternateRefsCommand' '
                        --format="%(objectname)" \
                        refs/heads/public/
        EOF
-       test_config -C fork core.alternateRefsCommand alternate-refs &&
+       test_config -C fork core.alternateRefsCommand ./alternate-refs &&
        git rev-parse public/branch >expect &&
        printf "0000" | git receive-pack fork >actual &&
        extract_haves <actual >actual.haves &&
index 7a8f56d..7316365 100755 (executable)
@@ -1577,7 +1577,13 @@ test_expect_success 'receive.denyCurrentBranch = updateInstead' '
                test $(git -C .. rev-parse master) = $(git rev-parse HEAD) &&
                git diff --quiet &&
                git diff --cached --quiet
-       )
+       ) &&
+
+       # (6) updateInstead intervened by fast-forward check
+       test_must_fail git push void master^:master &&
+       test $(git -C void rev-parse HEAD) = $(git rev-parse master) &&
+       git -C void diff --quiet &&
+       git -C void diff --cached --quiet
 '
 
 test_expect_success 'updateInstead with push-to-checkout hook' '
index 6391437..336f02a 100755 (executable)
@@ -166,6 +166,48 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack -
        grep "git index-pack.*--fsck-objects" trace
 '
 
+test_expect_success 'use fsck before and after manually fetching a missing subtree' '
+       # push new commit so server has a subtree
+       mkdir src/dir &&
+       echo "in dir" >src/dir/file.txt &&
+       git -C src add dir/file.txt &&
+       git -C src commit -m "file in dir" &&
+       git -C src push -u srv master &&
+       SUBTREE=$(git -C src rev-parse HEAD:dir) &&
+
+       rm -rf dst &&
+       git clone --no-checkout --filter=tree:0 "file://$(pwd)/srv.bare" dst &&
+       git -C dst fsck &&
+
+       # Make sure we only have commits, and all trees and blobs are missing.
+       git -C dst rev-list --missing=allow-any --objects master \
+               >fetched_objects &&
+       awk -f print_1.awk fetched_objects |
+       xargs -n1 git -C dst cat-file -t >fetched_types &&
+
+       sort -u fetched_types >unique_types.observed &&
+       echo commit >unique_types.expected &&
+       test_cmp unique_types.expected unique_types.observed &&
+
+       # Auto-fetch a tree with cat-file.
+       git -C dst cat-file -p $SUBTREE >tree_contents &&
+       grep file.txt tree_contents &&
+
+       # fsck still works after an auto-fetch of a tree.
+       git -C dst fsck &&
+
+       # Auto-fetch all remaining trees and blobs with --missing=error
+       git -C dst rev-list --missing=error --objects master >fetched_objects &&
+       test_line_count = 70 fetched_objects &&
+
+       awk -f print_1.awk fetched_objects |
+       xargs -n1 git -C dst cat-file -t >fetched_types &&
+
+       sort -u fetched_types >unique_types.observed &&
+       test_write_lines blob commit tree >unique_types.expected &&
+       test_cmp unique_types.expected unique_types.observed
+'
+
 test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
        rm -rf src dst &&
        git init src &&
index 53975c5..eb32505 100755 (executable)
@@ -34,6 +34,18 @@ test_expect_success 'verify blob:none omits all 5 blobs' '
        test_cmp expected observed
 '
 
+test_expect_success 'specify blob explicitly prevents filtering' '
+       file_3=$(git -C r1 ls-files -s file.3 |
+                awk -f print_2.awk) &&
+
+       file_4=$(git -C r1 ls-files -s file.4 |
+                awk -f print_2.awk) &&
+
+       git -C r1 rev-list --objects --filter=blob:none HEAD $file_3 >observed &&
+       grep "$file_3" observed &&
+       ! grep "$file_4" observed
+'
+
 test_expect_success 'verify emitted+omitted == all' '
        git -C r1 rev-list --objects HEAD >revs &&
        awk -f print_1.awk revs |
@@ -232,6 +244,56 @@ test_expect_success 'verify sparse:oid=oid-ish omits top-level files' '
        test_cmp expected observed
 '
 
+test_expect_success 'rev-list W/ --missing=print and --missing=allow-any for trees' '
+       TREE=$(git -C r3 rev-parse HEAD:dir1) &&
+
+       # Create a spare repo because we will be deleting objects from this one.
+       git clone r3 r3.b &&
+
+       rm r3.b/.git/objects/$(echo $TREE | sed "s|^..|&/|") &&
+
+       git -C r3.b rev-list --quiet --missing=print --objects HEAD \
+               >missing_objs 2>rev_list_err &&
+       echo "?$TREE" >expected &&
+       test_cmp expected missing_objs &&
+
+       # do not complain when a missing tree cannot be parsed
+       test_must_be_empty rev_list_err &&
+
+       git -C r3.b rev-list --missing=allow-any --objects HEAD \
+               >objs 2>rev_list_err &&
+       ! grep $TREE objs &&
+       test_must_be_empty rev_list_err
+'
+
+# Test tree:0 filter.
+
+test_expect_success 'verify tree:0 includes trees in "filtered" output' '
+       git -C r3 rev-list --quiet --objects --filter-print-omitted \
+               --filter=tree:0 HEAD >revs &&
+
+       awk -f print_1.awk revs |
+       sed s/~// |
+       xargs -n1 git -C r3 cat-file -t >unsorted_filtered_types &&
+
+       sort -u unsorted_filtered_types >filtered_types &&
+       test_write_lines blob tree >expected &&
+       test_cmp expected filtered_types
+'
+
+# Make sure tree:0 does not iterate through any trees.
+
+test_expect_success 'filter a GIANT tree through tree:0' '
+       GIT_TRACE=1 git -C r3 rev-list \
+               --objects --filter=tree:0 HEAD 2>filter_trace &&
+       grep "Skipping contents of tree [.][.][.]" filter_trace >actual &&
+       # One line for each commit traversed.
+       test_line_count = 2 actual &&
+
+       # Make sure no other trees were considered besides the root.
+       ! grep "Skipping contents of tree [^.]" filter_trace
+'
+
 # Delete some loose objects and use rev-list, but WITHOUT any filtering.
 # This models previously omitted objects that we did not receive.
 
index b2ca77b..5fcf281 100755 (executable)
@@ -112,7 +112,7 @@ do
 done
 
 test_expect_success 'editor with a space' '
-       echo "echo space >\$1" >"e space.sh" &&
+       echo "echo space >\"\$1\"" >"e space.sh" &&
        chmod a+x "e space.sh" &&
        GIT_EDITOR="./e\ space.sh" git commit --amend &&
        test space = "$(git show -s --pretty=format:%s)"
similarity index 99%
rename from t/t7500-commit.sh
rename to t/t7500-commit-template-squash-signoff.sh
index 31ab608..46a5cd4 100755 (executable)
@@ -5,7 +5,7 @@
 
 test_description='git commit
 
-Tests for selected commit options.'
+Tests for template, signoff, squash and -F functions.'
 
 . ./test-lib.sh
 
similarity index 98%
rename from t/t7509-commit.sh
rename to t/t7509-commit-authorship.sh
index ddef7ea..500ab2f 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2009 Erick Mattos
 #
 
-test_description='git commit --reset-author'
+test_description='commit tests of various authorhip options. '
 
 . ./test-lib.sh
 
index 48ec767..41c09f1 100755 (executable)
@@ -19,8 +19,10 @@ test_expect_success 'init depot' '
                p4 add file1 &&
                p4 submit -d "change 1" &&
                : >file_to_delete &&
+               : >file_to_move &&
                p4 add file_to_delete &&
-               p4 submit -d "file to delete"
+               p4 add file_to_move &&
+               p4 submit -d "add files to delete"
        )
 '
 
@@ -36,6 +38,8 @@ test_expect_success 'create shelved changelist' '
                echo "new file" >file2 &&
                p4 add file2 &&
                p4 delete file_to_delete &&
+               p4 edit file_to_move &&
+               p4 move file_to_move moved_file &&
                p4 opened &&
                p4 shelve -i <<EOF
 Change: new
@@ -47,6 +51,8 @@ Files:
        //depot/file1
        //depot/file2
        //depot/file_to_delete
+       //depot/file_to_move
+       //depot/moved_file
 EOF
 
        ) &&
@@ -54,12 +60,14 @@ EOF
                cd "$git" &&
                change=$(last_shelved_change) &&
                git p4 unshelve $change &&
-               git show refs/remotes/p4/unshelved/$change | grep -q "Further description" &&
-               git cherry-pick refs/remotes/p4/unshelved/$change &&
+               git show refs/remotes/p4-unshelved/$change | grep -q "Further description" &&
+               git cherry-pick refs/remotes/p4-unshelved/$change &&
                test_path_is_file file2 &&
                test_cmp file1 "$cli"/file1 &&
                test_cmp file2 "$cli"/file2 &&
-               test_path_is_missing file_to_delete
+               test_path_is_missing file_to_delete &&
+               test_path_is_missing file_to_move &&
+               test_path_is_file moved_file
        )
 '
 
@@ -88,10 +96,22 @@ EOF
                cd "$git" &&
                change=$(last_shelved_change) &&
                git p4 unshelve $change &&
-               git diff refs/remotes/p4/unshelved/$change.0 refs/remotes/p4/unshelved/$change | grep -q file3
+               git diff refs/remotes/p4-unshelved/$change.0 refs/remotes/p4-unshelved/$change | grep -q file3
        )
 '
 
+shelve_one_file () {
+       description="Change to be unshelved" &&
+       file="$1" &&
+       p4 shelve -i <<EOF
+Change: new
+Description:
+       $description
+Files:
+       $file
+EOF
+}
+
 # This is the tricky case where the shelved changelist base revision doesn't
 # match git-p4's idea of the base revision
 #
@@ -108,29 +128,52 @@ test_expect_success 'create shelved changelist based on p4 change ahead of p4/ma
                p4 submit -d "change:foo" &&
                p4 edit file1 &&
                echo "bar" >>file1 &&
-               p4 shelve -i <<EOF &&
-Change: new
-Description:
-       Change to be unshelved
-Files:
-       //depot/file1
-EOF
+               shelve_one_file //depot/file1 &&
                change=$(last_shelved_change) &&
-               p4 describe -S $change | grep -q "Change to be unshelved"
+               p4 describe -S $change >out.txt &&
+               grep -q "Change to be unshelved" out.txt
        )
 '
 
-# Now try to unshelve it. git-p4 should refuse to do so.
+# Now try to unshelve it.
 test_expect_success 'try to unshelve the change' '
        test_when_finished cleanup_git &&
        (
                change=$(last_shelved_change) &&
                cd "$git" &&
-               test_must_fail git p4 unshelve $change 2>out.txt &&
-               grep -q "cannot unshelve" out.txt
+               git p4 unshelve $change >out.txt &&
+               grep -q "unshelved changelist $change" out.txt
        )
 '
 
+# Specify the origin. Create 2 unrelated files, and check that
+# we only get the one in HEAD~, not the one in HEAD.
+
+test_expect_success 'unshelve specifying the origin' '
+       (
+               cd "$cli" &&
+               : >unrelated_file0 &&
+               p4 add unrelated_file0 &&
+               p4 submit -d "unrelated" &&
+               : >unrelated_file1 &&
+               p4 add unrelated_file1 &&
+               p4 submit -d "unrelated" &&
+               : >file_to_shelve &&
+               p4 add file_to_shelve &&
+               shelve_one_file //depot/file_to_shelve
+       ) &&
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot/@all &&
+       (
+               cd "$git" &&
+               change=$(last_shelved_change) &&
+               git p4 unshelve --origin HEAD~ $change &&
+               git checkout refs/remotes/p4-unshelved/$change &&
+               test_path_is_file unrelated_file0 &&
+               test_path_is_missing unrelated_file1 &&
+               test_path_is_file file_to_shelve
+       )
+'
 test_expect_success 'kill p4d' '
        kill_p4d
 '
diff --git a/tag.c b/tag.c
index 1db663d..7445b8f 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -144,7 +144,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
                return 0;
        item->object.parsed = 1;
 
-       if (size < GIT_SHA1_HEXSZ + 24)
+       if (size < the_hash_algo->hexsz + 24)
                return -1;
        if (memcmp("object ", bufptr, 7) || parse_oid_hex(bufptr + 7, &oid, &bufptr) || *bufptr++ != '\n')
                return -1;
index f4ffbd9..01ce11a 100644 (file)
@@ -1413,9 +1413,9 @@ static void read_alternate_refs(const char *path,
        fh = xfdopen(cmd.out, "r");
        while (strbuf_getline_lf(&line, fh) != EOF) {
                struct object_id oid;
+               const char *p;
 
-               if (get_oid_hex(line.buf, &oid) ||
-                   line.buf[GIT_SHA1_HEXSZ]) {
+               if (parse_oid_hex(line.buf, &oid, &p) || *p) {
                        warning(_("invalid line while parsing alternate refs: %s"),
                                line.buf);
                        break;
index 51bfac6..7570df4 100644 (file)
@@ -380,7 +380,7 @@ static int check_updates(struct unpack_trees_options *o)
 {
        unsigned cnt = 0;
        int errs = 0;
-       struct progress *progress = NULL;
+       struct progress *progress;
        struct index_state *index = &o->result;
        struct checkout state = CHECKOUT_INIT;
        int i;
index 540778d..5dc317c 100644 (file)
@@ -444,6 +444,7 @@ static int do_reachable_revlist(struct child_process *cmd,
        struct object *o;
        char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */
        int i;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        cmd->argv = argv;
        cmd->git_cmd = 1;
@@ -462,7 +463,7 @@ static int do_reachable_revlist(struct child_process *cmd,
                goto error;
 
        namebuf[0] = '^';
-       namebuf[GIT_SHA1_HEXSZ + 1] = '\n';
+       namebuf[hexsz + 1] = '\n';
        for (i = get_max_object_index(); 0 < i; ) {
             &nbs