Merge branch 'lb/contrib-contacts-looser-diff-parsing'
authorJunio C Hamano <gitster@pobox.com>
Fri, 14 Mar 2014 21:24:59 +0000 (14:24 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 14 Mar 2014 21:24:59 +0000 (14:24 -0700)
* lb/contrib-contacts-looser-diff-parsing:
git-contacts: do not fail parsing of good diffs

178 files changed:
.gitignore
Documentation/RelNotes/2.0.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-blame.txt
Documentation/git-cherry-pick.txt
Documentation/git-clone.txt
Documentation/git-commit-tree.txt
Documentation/git-commit.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-repack.txt
Documentation/git-reset.txt
Documentation/git-rev-list.txt
Documentation/git-revert.txt
Documentation/git-submodule.txt
Documentation/gitignore.txt
Documentation/gitmodules.txt
Documentation/merge-strategies.txt
Documentation/rev-list-options.txt
Documentation/technical/api-hash.txt [deleted file]
Documentation/technical/api-hashmap.txt [new file with mode: 0644]
Documentation/technical/bitmap-format.txt [new file with mode: 0644]
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt
Makefile
RelNotes
advice.c
advice.h
bisect.c
block-sha1/sha1.c
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/check-attr.c
builtin/checkout.c
builtin/clean.c
builtin/commit-tree.c
builtin/commit.c
builtin/config.c
builtin/describe.c
builtin/fetch.c
builtin/gc.c
builtin/grep.c
builtin/ls-files.c
builtin/ls-tree.c
builtin/merge.c
builtin/mv.c
builtin/pack-objects.c
builtin/push.c
builtin/repack.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/rm.c
builtin/update-index.c
cache.h
combine-diff.c
compat/bswap.h
config.c
contrib/completion/git-completion.bash
contrib/git-resurrect.sh
daemon.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-order.c
diffcore-rename.c
diffcore.h
dir.c
dir.h
ewah/bitmap.c [new file with mode: 0644]
ewah/ewah_bitmap.c [new file with mode: 0644]
ewah/ewah_io.c [new file with mode: 0644]
ewah/ewah_rlw.c [new file with mode: 0644]
ewah/ewok.h [new file with mode: 0644]
ewah/ewok_rlw.h [new file with mode: 0644]
fetch-pack.c
git-am.sh
git-compat-util.h
git-instaweb.sh
git-pull.sh
git-quiltimport.sh
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase--merge.sh
git-rebase.sh
git-request-pull.sh
git-sh-setup.sh
git-submodule.sh
hash.c [deleted file]
hash.h [deleted file]
hashmap.c [new file with mode: 0644]
hashmap.h [new file with mode: 0644]
http.c
http.h
khash.h [new file with mode: 0644]
line-log.c
merge-recursive.c
name-hash.c
notes-utils.c
pack-bitmap-write.c [new file with mode: 0644]
pack-bitmap.c [new file with mode: 0644]
pack-bitmap.h [new file with mode: 0644]
pack-objects.c [new file with mode: 0644]
pack-objects.h [new file with mode: 0644]
pack-revindex.c
pack-revindex.h
pack-write.c
path.c
pathspec.c
po/fr.po
preload-index.c
read-cache.c
remote-curl.c
rerere.c
resolve-undo.c
revision.c
revision.h
sequencer.c
sequencer.h
setup.c
sha1_file.c
strbuf.c
streaming.c
submodule.c
t/lib-git-daemon.sh
t/lib-httpd.sh
t/perf/p5310-pack-bitmaps.sh [new file with mode: 0755]
t/t0003-attributes.sh
t/t0008-ignores.sh
t/t0011-hashmap.sh [new file with mode: 0755]
t/t0060-path-utils.sh
t/t1300-repo-config.sh
t/t1305-config-include.sh
t/t2200-add-update.sh
t/t3004-ls-files-basic.sh
t/t3030-merge-recursive.sh
t/t4010-diff-pathspec.sh
t/t4035-diff-quiet.sh
t/t4056-diff-order.sh
t/t4057-diff-combined-paths.sh [new file with mode: 0755]
t/t5310-pack-bitmaps.sh [new file with mode: 0755]
t/t5400-send-pack.sh
t/t5537-fetch-shallow.sh
t/t5538-push-shallow.sh
t/t5539-fetch-http-shallow.sh [new file with mode: 0755]
t/t5540-http-push-webdav.sh [new file with mode: 0755]
t/t5540-http-push.sh [deleted file]
t/t5541-http-push-smart.sh [new file with mode: 0755]
t/t5541-http-push.sh [deleted file]
t/t5550-http-fetch-dumb.sh [new file with mode: 0755]
t/t5550-http-fetch.sh [deleted file]
t/t5551-http-fetch-smart.sh [new file with mode: 0755]
t/t5551-http-fetch.sh [deleted file]
t/t5561-http-backend.sh
t/t5570-git-daemon.sh
t/t6131-pathspec-icase.sh
t/t7101-reset-empty-subdirs.sh [new file with mode: 0755]
t/t7101-reset.sh [deleted file]
t/t7102-reset.sh
t/t7104-reset-hard.sh [new file with mode: 0755]
t/t7104-reset.sh [deleted file]
t/t7406-submodule-update.sh
t/t7510-signed-commit.sh
t/t7601-merge-pull-config.sh
t/test-lib-functions.sh
test-hashmap.c [new file with mode: 0644]
tree-diff.c
tree-walk.c
tree-walk.h
unpack-trees.c
utf8.c
wt-status.c
index b5f9defed37c43b2c6075d7065c8cbae2b1797e1..dc600f9b36d09f0668064e044520c7ce633f09d8 100644 (file)
 /test-dump-cache-tree
 /test-scrap-cache-tree
 /test-genrandom
+/test-hashmap
 /test-index-version
 /test-line-buffer
 /test-match-trees
diff --git a/Documentation/RelNotes/2.0.0.txt b/Documentation/RelNotes/2.0.0.txt
new file mode 100644 (file)
index 0000000..acc7415
--- /dev/null
@@ -0,0 +1,146 @@
+Git v2.0 Release Notes
+======================
+
+Backward compatibility notes
+----------------------------
+
+When "git push [$there]" does not say what to push, we have used the
+traditional "matching" semantics so far (all your branches were sent
+to the remote as long as there already are branches of the same name
+over there).  In Git 2.0, the default is now the "simple" semantics,
+which pushes:
+
+ - only the current branch to the branch with the same name, and only
+   when the current branch is set to integrate with that remote
+   branch, if you are pushing to the same remote as you fetch from; or
+
+ - only the current branch to the branch with the same name, if you
+   are pushing to a remote that is not where you usually fetch from.
+
+You can use the configuration variable "push.default" to change
+this.  If you are an old-timer who wants to keep using the
+"matching" semantics, you can set the variable to "matching", for
+example.  Read the documentation for other possibilities.
+
+When "git add -u" and "git add -A" are run inside a subdirectory
+without specifying which paths to add on the command line, they
+operate on the entire tree for consistency with "git commit -a" and
+other commands (these commands used to operate only on the current
+subdirectory).  Say "git add -u ." or "git add -A ." if you want to
+limit the operation to the current directory.
+
+"git add <path>" is the same as "git add -A <path>" now, so that
+"git add dir/" will notice paths you removed from the directory and
+record the removal.  In older versions of Git, "git add <path>" used
+to ignore removals.  You can say "git add --ignore-removal <path>" to
+add only added or modified paths in <path>, if you really want to.
+
+The "-q" option to "git diff-files", which does *NOT* mean "quiet",
+has been removed (it told Git to ignore deletion, which you can do
+with "git diff-files --diff-filter=d").
+
+
+Updates since v1.9 series
+-------------------------
+
+Foreign interfaces, subsystems and ports.
+
+
+UI, Workflows & Features
+
+ * The "simple" mode is the default for "git push".
+
+ * "git add -u" and "git add -A", when run without any pathspec, is a
+   tree-wide operation even when run inside a subdirectory of a
+   working tree.
+
+ * "git add <path> is the same as "git add -A <path>" now.
+
+ * "core.statinfo" configuration variable, which is a
+   never-advertised synonym to "core.checkstat", has been removed.
+
+ * The "-q" option to "git diff-files", which does *NOT* mean
+   "quiet", has been removed (it told Git to ignore deletion, which
+   you can do with "git diff-files --diff-filter=d").
+
+ * Many commands that creates commits, e.g. "pull", "rebase",
+   learned to take the --gpg-sign option on the command line.
+
+ * "git commit" can be told to always GPG sign the resulting commit
+   by setting "commit.gpgsign" configuration variable to true (the
+   command line option --no-gpg-sign should override it).
+
+ * "git pull" can be told to only accept fast-forward by setting the
+   new "pull.ff" configuration.
+
+ * "git reset" learned "-N" option, which does not reset the index
+   fully for paths the index knows about but the tree-ish the command
+   resets to does not (these paths are kept as intend-to-add entries).
+
+ * Newly cloned submodule repositories by "git submodule update",
+   when the "checkout" update mode is used, will be on a local
+   branch instead of on a detached HEAD, just like submodules added
+   with "git submodule add".
+
+
+Performance, Internal Implementation, etc.
+
+ * The bitmap-index feature from JGit has been ported, which should
+   significantly improve performance when serving objects form a
+   repository that uses it.
+
+ * The way "git log --cc" shows a combined diff against multiple
+   parents have been optimized.
+
+ * The prefixcmp() and suffixcmp() functions are gone.  Use
+   starts_with() and ends_with(), and also consider if skip_prefix()
+   suits your needs better when using the former.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v1.9 series
+-----------------------
+
+Unless otherwise noted, all the fixes since v1.9 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * "merge-recursive" was broken in 1.7.7 era and stopped working in
+   an empty (temporary) working tree, when there are renames
+   involved.  This has been corrected.
+   (merge 6e2068a bk/refresh-missing-ok-in-merge-recursive later to maint.)
+
+ * "git rev-parse" was loose in rejecting command line arguments
+   that do not make sense, e.g. "--default" without the required
+   value for that option.
+   (merge a43219f ds/rev-parse-required-args later to maint.)
+
+ * include.path variable (or any variable that expects a path that
+   can use ~username expansion) in the configuration file is not a
+   boolean, but the code failed to check it.
+   (merge 67beb60 jk/config-path-include-fix later to maint.)
+
+ * Commands that take pathspecs on the command line misbehaved when
+   the pathspec is given as an absolute pathname (which is a
+   practice not particularly encouraged) that points at a symbolic
+   link in the working tree.
+   (merge later 655ee9e mw/symlinks to maint.)
+
+ * "git diff --quiet -- pathspec1 pathspec2" sometimes did not return
+   correct status value.
+   (merge f34b205 nd/diff-quiet-stat-dirty later to maint.)
+
+ * Attempting to deepen a shallow repository by fetching over smart
+   HTTP transport failed in the protocol exchange, when no-done
+   extension was used.  The fetching side waited for the list of
+   shallow boundary commits after the sending end stopped talking to
+   it.
+   (merge 0232852 nd/http-fetch-shallow-fix later to maint.)
+
+ * Allow "git cmd path/", when the 'path' is where a submodule is
+   bound to the top-level working tree, to match 'path', despite the
+   extra and unnecessary trailing slash (such a slash is often
+   given by command line completion).
+   (merge 2e70c01 nd/submodule-pathspec-ending-with-slash later to maint.)
index 5f4d7939ed1ec267e7f282624e5bb480e72d7a33..73904bce55ff1d78bd10a5eb6b83f5fdf36ea74a 100644 (file)
@@ -142,19 +142,13 @@ advice.*::
 --
        pushUpdateRejected::
                Set this variable to 'false' if you want to disable
-               'pushNonFFCurrent', 'pushNonFFDefault',
+               'pushNonFFCurrent',
                'pushNonFFMatching', 'pushAlreadyExists',
                'pushFetchFirst', and 'pushNeedsForce'
                simultaneously.
        pushNonFFCurrent::
                Advice shown when linkgit:git-push[1] fails due to a
                non-fast-forward update to the current branch.
-       pushNonFFDefault::
-               Advice to set 'push.default' to 'upstream' or 'current'
-               when you ran linkgit:git-push[1] and pushed 'matching
-               refs' by default (i.e. you did not provide an explicit
-               refspec, and no 'push.default' configuration was set)
-               and it resulted in a non-fast-forward error.
        pushNonFFMatching::
                Advice shown when you ran linkgit:git-push[1] and pushed
                'matching refs' explicitly (i.e. you used ':', or
@@ -992,6 +986,14 @@ commit.cleanup::
        have to remove the help lines that begin with `#` in the commit log
        template yourself, if you do this).
 
+commit.gpgsign::
+
+       A boolean to specify whether all commits should be GPG signed.
+       Use of this option when doing operations such as rebase can
+       result in a large number of commits being signed. It may be
+       convenient to use an agent to avoid typing your GPG passphrase
+       several times.
+
 commit.status::
        A boolean to enable/disable inclusion of status information in the
        commit message template when using an editor to prepare the commit
@@ -1167,6 +1169,10 @@ gc.autopacklimit::
        --auto` consolidates them into one larger pack.  The
        default value is 50.  Setting this to 0 disables it.
 
+gc.autodetach::
+       Make `git gc --auto` return immediately andrun in background
+       if the system supports it. Default is true.
+
 gc.packrefs::
        Running `git pack-refs` in a repository renders it
        unclonable by Git versions prior to 1.5.1.2 over dumb
@@ -1862,6 +1868,31 @@ pack.packSizeLimit::
        Common unit suffixes of 'k', 'm', or 'g' are
        supported.
 
+pack.useBitmaps::
+       When true, git will use pack bitmaps (if available) when packing
+       to stdout (e.g., during the server side of a fetch). Defaults to
+       true. You should not generally need to turn this off unless
+       you are debugging pack bitmaps.
+
+pack.writebitmaps::
+       When true, git will write a bitmap index when packing all
+       objects to disk (e.g., when `git repack -a` is run).  This
+       index can speed up the "counting objects" phase of subsequent
+       packs created for clones and fetches, at the cost of some disk
+       space and extra time spent on the initial repack.  Defaults to
+       false.
+
+pack.writeBitmapHashCache::
+       When true, git will include a "hash cache" section in the bitmap
+       index (if one is written). This cache can be used to feed git's
+       delta heuristics, potentially leading to better deltas between
+       bitmapped and non-bitmapped objects (e.g., when serving a fetch
+       between an older, bitmapped pack and objects that have been
+       pushed since the last gc). The downside is that it consumes 4
+       bytes per object of disk space, and that JGit's bitmap
+       implementation does not understand it, causing it to complain if
+       Git and JGit are used on the same repository. Defaults to false.
+
 pager.<cmd>::
        If the value is boolean, turns on or off pagination of the
        output of a particular Git subcommand when writing to a tty.
@@ -1881,6 +1912,16 @@ pretty.<name>::
        Note that an alias with the same name as a built-in format
        will be silently ignored.
 
+pull.ff::
+       By default, Git does not create an extra merge commit when merging
+       a commit that is a descendant of the current commit. Instead, the
+       tip of the current branch is fast-forwarded. When set to `false`,
+       this variable tells Git to create an extra merge commit in such
+       a case (equivalent to giving the `--no-ff` option from the command
+       line). When set to `only`, only such fast-forward merges are
+       allowed (equivalent to giving the `--ff-only` option from the
+       command line).
+
 pull.rebase::
        When true, rebase branches on top of the fetched branch, instead
        of merging the default branch from the default remote when "git
@@ -1933,7 +1974,7 @@ When pushing to a remote that is different from the remote you normally
 pull from, work as `current`.  This is the safest option and is suited
 for beginners.
 +
-This mode will become the default in Git 2.0.
+This mode has become the default in Git 2.0.
 
 * `matching` - push all branches having the same name on both ends.
   This makes the repository you are pushing to remember the set of
@@ -1952,8 +1993,8 @@ suitable for pushing into a shared central repository, as other
 people may add new branches there, or update the tip of existing
 branches outside your control.
 +
-This is currently the default, but Git 2.0 will change the default
-to `simple`.
+This used to be the default, but not since Git 2.0 (`simple` is the
+new default).
 
 --
 
index 48754cbc67a2cc3819fac272cdef9d9d0d76c0d7..895922e27cc4250d26818b4ca4ba42f1d2004fed 100644 (file)
@@ -53,8 +53,14 @@ OPTIONS
        Files to add content from.  Fileglobs (e.g. `*.c`) can
        be given to add all matching files.  Also a
        leading directory name (e.g. `dir` to add `dir/file1`
-       and `dir/file2`) can be given to add all files in the
-       directory, recursively.
+       and `dir/file2`) can be given to update the index to
+       match the current state of the directory as a whole (e.g.
+       specifying `dir` will record not just a file `dir/file1`
+       modified in the working tree, a file `dir/file2` added to
+       the working tree, but also a file `dir/file3` removed from
+       the working tree.  Note that older versions of Git used
+       to ignore removed files; use `--no-all` option if you want
+       to add modified or new files but ignore removed ones.
 
 -n::
 --dry-run::
@@ -104,10 +110,10 @@ apply to the index. See EDITING PATCHES below.
        <pathspec>.  This removes as well as modifies index entries to
        match the working tree, but adds no new files.
 +
-If no <pathspec> is given, the current version of Git defaults to
-"."; in other words, update all tracked files in the current directory
-and its subdirectories. This default will change in a future version
-of Git, hence the form without <pathspec> should not be used.
+If no <pathspec> is given when `-u` option is used, all
+tracked files in the entire working tree are updated (old versions
+of Git used to limit the update to the current directory and its
+subdirectories).
 
 -A::
 --all::
@@ -117,10 +123,10 @@ of Git, hence the form without <pathspec> should not be used.
        entry.  This adds, modifies, and removes index entries to
        match the working tree.
 +
-If no <pathspec> is given, the current version of Git defaults to
-"."; in other words, update all files in the current directory
-and its subdirectories. This default will change in a future version
-of Git, hence the form without <pathspec> should not be used.
+If no <pathspec> is given when `-A` option is used, all
+files in the entire working tree are updated (old versions
+of Git used to limit the update to the current directory and its
+subdirectories).
 
 --no-all::
 --ignore-removal::
@@ -129,11 +135,9 @@ of Git, hence the form without <pathspec> should not be used.
        files that have been removed from the working tree.  This
        option is a no-op when no <pathspec> is used.
 +
-This option is primarily to help the current users of Git, whose
-"git add <pathspec>..." ignores removed files.  In future versions
-of Git, "git add <pathspec>..." will be a synonym to "git add -A
-<pathspec>..." and "git add --ignore-removal <pathspec>..." will behave like
-today's "git add <pathspec>...", ignoring removed files.
+This option is primarily to help users who are used to older
+versions of Git, whose "git add <pathspec>..." was a synonym
+for "git add --no-all <pathspec>...", i.e. ignored removed files.
 
 -N::
 --intent-to-add::
index 54d8461d61b2f2ad5dbf5ce8c9106534d1a74289..17924d0f3ff3cf525b5ab93b50020929d6ea24c0 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
         [--ignore-date] [--ignore-space-change | --ignore-whitespace]
         [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
         [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
-        [--[no-]scissors]
+        [--[no-]scissors] [-S[<keyid>]]
         [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort)
 
@@ -119,6 +119,10 @@ default.   You can use `--no-utf8` to override this.
        Skip the current patch.  This is only meaningful when
        restarting an aborted patch.
 
+-S[<keyid>]::
+--gpg-sign[=<keyid>]::
+       GPG-sign commits.
+
 --continue::
 -r::
 --resolved::
index 8e70a61840ba59d75bf64cb14b08df9c0961ee63..9f23a861ce3a8dfd009532b469290cb6470b500d 100644 (file)
@@ -35,7 +35,8 @@ Apart from supporting file annotation, Git also supports searching the
 development history for when a code snippet occurred in a change. This makes it
 possible to track when a code snippet was added to a file, moved or copied
 between files, and eventually deleted or replaced. It works by searching for
-a text string in the diff. A small example:
+a text string in the diff. A small example of the pickaxe interface
+that searches for `blame_usage`:
 
 -----------------------------------------------------------------------------
 $ git log --pretty=oneline -S'blame_usage'
index c205d2363e426ed6b1a91df2b8d9b4a3c576a5ea..f1e6b2fd6d15809f5180fe705c3d571bf247ff1d 100644 (file)
@@ -8,7 +8,8 @@ git-cherry-pick - Apply the changes introduced by some existing commits
 SYNOPSIS
 --------
 [verse]
-'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
+'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]
+                 [-S[<keyid>]] <commit>...
 'git cherry-pick' --continue
 'git cherry-pick' --quit
 'git cherry-pick' --abort
@@ -100,6 +101,10 @@ effect to your index in a row.
 --signoff::
        Add Signed-off-by line at the end of the commit message.
 
+-S[<keyid>]::
+--gpg-sign[=<keyid>]::
+       GPG-sign commits.
+
 --ff::
        If the current HEAD is the same as the parent of the
        cherry-pick'ed commit, then a fast forward to this commit will
index bf3dac0ceffdaa6490f30a507d11cff2d199d9de..0363d0039b274093e6d230a83fcf99c3ff6700d8 100644 (file)
@@ -55,15 +55,12 @@ repository is specified as a URL, then this flag is ignored (and we
 never use the local optimizations).  Specifying `--no-local` will
 override the default when `/path/to/repo` is given, using the regular
 Git transport instead.
-+
-To force copying instead of hardlinking (which may be desirable if you
-are trying to make a back-up of your repository), but still avoid the
-usual "Git aware" transport mechanism, `--no-hardlinks` can be used.
 
 --no-hardlinks::
-       Optimize the cloning process from a repository on a
-       local filesystem by copying files under `.git/objects`
-       directory.
+       Force the cloning process from a repository on a local
+       filesystem to copy the files under the `.git/objects`
+       directory instead of using hardlinks. This may be desirable
+       if you are trying to make a back-up of your repository.
 
 --shared::
 -s::
index cafdc9642d312776b0122c4d010a9e9246bad8ff..a469eab06683dece65f447a6c2691c30fe54b214 100644 (file)
@@ -55,8 +55,13 @@ OPTIONS
        from the standard input.
 
 -S[<keyid>]::
+--gpg-sign[=<keyid>]::
        GPG-sign commit.
 
+--no-gpg-sign::
+       Countermand `commit.gpgsign` configuration variable that is
+       set to force each and every commit to be signed.
+
 
 Commit Information
 ------------------
index 1a7616c73a204f69bca98d0ff7f4cdfda20ba420..7c42e9cabcb250b93cd286e8efe23df90ea0f71c 100644 (file)
@@ -302,6 +302,10 @@ configuration variable documented in linkgit:git-config[1].
 --gpg-sign[=<keyid>]::
        GPG-sign commit.
 
+--no-gpg-sign::
+       Countermand `commit.gpgsign` configuration variable that is
+       set to force each and every commit to be signed.
+
 \--::
        Do not interpret any more arguments as options.
 
index 2b7f4f939f38626dbd796b808cc21285ad5c8c3d..14862fb203b8c269fc391eee886520b1675e540a 100644 (file)
@@ -83,8 +83,8 @@ the local side, the remote side is updated if a branch of the same name
 already exists on the remote side.
 
 --all::
-       Instead of naming each ref to push, specifies that all
-       refs under `refs/heads/` be pushed.
+       Push all branches (i.e. refs under `refs/heads/`); cannot be
+       used with other <refspec>.
 
 --prune::
        Remove remote branches that don't have a local counterpart. For example
@@ -442,8 +442,10 @@ Examples
        configured for the current branch).
 
 `git push origin`::
-       Without additional configuration, works like
-       `git push origin :`.
+       Without additional configuration, pushes the current branch to
+       the configured upstream (`remote.origin.merge` configuration
+       variable) if it has the same name as the current branch, and
+       errors out without pushing otherwise.
 +
 The default behavior of this command when no <refspec> is given can be
 configured by setting the `push` option of the remote, or the `push.default`
index 2889be6bdc4a1a243585e43def2e7416a9aa7038..2a93c645bdb62c28ac9315659b794aa1d3932a4b 100644 (file)
@@ -281,6 +281,10 @@ which makes little sense.
        specified, `-s recursive`.  Note the reversal of 'ours' and
        'theirs' as noted above for the `-m` option.
 
+-S[<keyid>]::
+--gpg-sign[=<keyid>]::
+       GPG-sign commits.
+
 -q::
 --quiet::
        Be quiet. Implies --no-stat.
index 2507c8bd913224b21118fe38d648f2538a06c387..cb103c8b6f8fe4fd89ad5e1a37b551f9f54d75c7 100644 (file)
@@ -3,7 +3,7 @@ git-remote(1)
 
 NAME
 ----
-git-remote - manage set of tracked repositories
+git-remote - Manage set of tracked repositories
 
 
 SYNOPSIS
index 509cf73e50542758d0ef3930f56fdb5dd0183aac..002cfd5eb959ce7c8afea32388477f3a1062c485 100644 (file)
@@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository
 SYNOPSIS
 --------
 [verse]
-'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [--window=<n>] [--depth=<n>]
+'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>]
 
 DESCRIPTION
 -----------
@@ -110,6 +110,13 @@ other objects in that pack they already have locally.
        The default is unlimited, unless the config variable
        `pack.packSizeLimit` is set.
 
+-b::
+--write-bitmap-index::
+       Write a reachability bitmap index as part of the repack. This
+       only makes sense when used with `-a` or `-A`, as the bitmaps
+       must be able to refer to all reachable objects. This option
+       overrides the setting of `pack.writebitmaps`.
+
 
 Configuration
 -------------
index f445cb38fa26a6daa62b5be7f65122948ca669ab..a077ba0ddc81cf499d38f8b541406ac4583bcd1e 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git reset' [-q] [<tree-ish>] [--] <paths>...
 'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
-'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
+'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
@@ -60,6 +60,9 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
        Resets the index but not the working tree (i.e., the changed files
        are preserved but not marked for commit) and reports what has not
        been updated. This is the default action.
++
+If `-N` is specified, removed paths are marked as intent-to-add (see
+linkgit:git-add[1]).
 
 --hard::
        Resets the index and working tree. Any changes to tracked files in the
index 045b37b82e7544ddb753cdec7ead2bc8d97ad1c8..7a1585def0ce0c0ca78a2735f619f0e8bdf3506c 100644 (file)
@@ -55,6 +55,7 @@ SYNOPSIS
             [ \--reverse ]
             [ \--walk-reflogs ]
             [ \--no-walk ] [ \--do-walk ]
+            [ \--use-bitmap-index ]
             <commit>... [ \-- <paths>... ]
 
 DESCRIPTION
index 2de67a54962b2f04f1f497344286fd17918433f3..9eb83f01a4513ae3503258067133cae99189d39e 100644 (file)
@@ -8,7 +8,7 @@ git-revert - Revert some existing commits
 SYNOPSIS
 --------
 [verse]
-'git revert' [--[no-]edit] [-n] [-m parent-number] [-s] <commit>...
+'git revert' [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>...
 'git revert' --continue
 'git revert' --quit
 'git revert' --abort
@@ -80,6 +80,10 @@ more details.
 This is useful when reverting more than one commits'
 effect to your index in a row.
 
+-S[<keyid>]::
+--gpg-sign[=<keyid>]::
+       GPG-sign commits.
+
 -s::
 --signoff::
        Add Signed-off-by line at the end of the commit message.
index bfef8a0c62a56865cfe2335841efcb4535642287..21cb59a6d6d9dd87754e2b20ec47bc67147370c7 100644 (file)
@@ -15,8 +15,8 @@ SYNOPSIS
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
-             [-f|--force] [--rebase] [--reference <repository>] [--depth <depth>]
-             [--merge] [--recursive] [--] [<path>...]
+             [-f|--force] [--rebase|--merge|--checkout] [--reference <repository>]
+             [--depth <depth>] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
              [commit] [--] [<path>...]
 'git submodule' [--quiet] foreach [--recursive] <command>
@@ -155,13 +155,31 @@ it contains local modifications.
 
 update::
        Update the registered submodules, i.e. clone missing submodules and
-       checkout the commit specified in the index of the containing repository.
-       This will make the submodules HEAD be detached unless `--rebase` or
-       `--merge` is specified or the key `submodule.$name.update` is set to
-       `rebase`, `merge` or `none`. `none` can be overridden by specifying
-       `--checkout`. Setting the key `submodule.$name.update` to `!command`
-       will cause `command` to be run. `command` can be any arbitrary shell
-       command that takes a single argument, namely the sha1 to update to.
+       checkout the commit specified in the index of the containing
+       repository.  The update mode defaults to `checkout`, but can be
+       configured with the `submodule.<name>.update` setting or the
+       `--rebase`, `--merge`, or `--checkout` options.
++
+For updates that clone missing submodules, checkout-mode updates will
+create submodules with detached HEADs; all other modes will create
+submodules with a local branch named after `submodule.<path>.branch`.
++
+For updates that do not clone missing submodules, the submodule's HEAD
+is only touched when the remote reference does not match the
+submodule's HEAD (for none-mode updates, the submodule is never
+touched).  The remote reference is usually the gitlinked commit from
+the superproject's tree, but with `--remote` it is the upstream
+subproject's `submodule.<name>.branch`.  This remote reference is
+integrated with the submodule's HEAD using the specified update mode.
+For checkout-mode updates, that will result in a detached HEAD.  For
+rebase- and merge-mode updates, the commit referenced by the
+submodule's HEAD may change, but the symbolic reference will remain
+unchanged (i.e. checked-out branches will still be checked-out
+branches, and detached HEADs will still be detached HEADs).  If none
+of the builtin modes fit your needs, set `submodule.<name>.update` to
+`!command` to configure a custom integration command.  `command` can
+be any arbitrary shell command that takes a single argument, namely
+the sha1 to update to.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
@@ -281,6 +299,16 @@ In order to ensure a current tracking branch state, `update --remote`
 fetches the submodule's remote repository before calculating the
 SHA-1.  If you don't want to fetch, you should use `submodule update
 --remote --no-fetch`.
++
+Use this option to integrate changes from the upstream subproject with
+your submodule's current HEAD.  Alternatively, you can run `git pull`
+from the submodule, which is equivalent except for the remote branch
+name: `update --remote` uses the default upstream repository and
+`submodule.<name>.branch`, while `git pull` uses the submodule's
+`branch.<name>.merge`.  Prefer `submodule.<name>.branch` if you want
+to distribute the default upstream branch with the superproject and
+`branch.<name>.merge` if you want a more native feel while working in
+the submodule itself.
 
 -N::
 --no-fetch::
index b08d34d84ec92eec6aa54572e3823062424c23bf..8734c1566ca918e8c62f759aebdf7d0b497e860b 100644 (file)
@@ -77,6 +77,9 @@ PATTERN FORMAT
    Put a backslash ("`\`") in front of the first hash for patterns
    that begin with a hash.
 
+ - Trailing spaces are ignored unless they are quoted with backlash
+   ("`\`").
+
  - An optional prefix "`!`" which negates the pattern; any
    matching file excluded by a previous pattern will become
    included again. It is not possible to re-include a file if a parent
index 347a9f76ee809c3691dc6580b0fba3ebde0b0e3c..f539e3f66a8b4c9b2603b0e2f63a89dd2adf1bfe 100644 (file)
@@ -55,6 +55,10 @@ submodule.<name>.branch::
        A remote branch name for tracking updates in the upstream submodule.
        If the option is not specified, it defaults to 'master'.  See the
        `--remote` documentation in linkgit:git-submodule[1] for details.
++
+This branch name is also used for the local branch created by
+non-checkout cloning updates.  See the `update` documentation in
+linkgit:git-submodule[1] for details.
 
 submodule.<name>.fetchRecurseSubmodules::
        This option can be used to control recursive fetching of this
index fb6e593e7c6f287612f30be6206c0492d77e38d3..350949810e224c9963278cbc73351011448e9b56 100644 (file)
@@ -20,7 +20,7 @@ recursive::
        merged tree of the common ancestors and uses that as
        the reference tree for the 3-way merge.  This has been
        reported to result in fewer merge conflicts without
-       causing mis-merges by tests done on actual merge commits
+       causing mismerges by tests done on actual merge commits
        taken from Linux 2.6 kernel development history.
        Additionally this can detect and handle merges involving
        renames.  This is the default merge strategy when
index 03533af7152e8b137cd3d885de3ec273b91d4791..9a3da3646e58f6155a6ff3397eddfd239703447a 100644 (file)
@@ -257,6 +257,14 @@ See also linkgit:git-reflog[1].
        Output excluded boundary commits. Boundary commits are
        prefixed with `-`.
 
+ifdef::git-rev-list[]
+--use-bitmap-index::
+
+       Try to speed up the traversal using the pack bitmap index (if
+       one is available). Note that when traversing with `--objects`,
+       trees and blobs will not have their associated path printed.
+endif::git-rev-list[]
+
 --
 
 History Simplification
diff --git a/Documentation/technical/api-hash.txt b/Documentation/technical/api-hash.txt
deleted file mode 100644 (file)
index e5061e0..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-hash API
-========
-
-The hash API is a collection of simple hash table functions. Users are expected
-to implement their own hashing.
-
-Data Structures
----------------
-
-`struct hash_table`::
-
-       The hash table structure. The `array` member points to the hash table
-       entries. The `size` member counts the total number of valid and invalid
-       entries in the table. The `nr` member keeps track of the number of
-       valid entries.
-
-`struct hash_table_entry`::
-
-       An opaque structure representing an entry in the hash table. The `hash`
-       member is the entry's hash key and the `ptr` member is the entry's
-       value.
-
-Functions
----------
-
-`init_hash`::
-
-       Initialize the hash table.
-
-`free_hash`::
-
-       Release memory associated with the hash table.
-
-`insert_hash`::
-
-       Insert a pointer into the hash table. If an entry with that hash
-       already exists, a pointer to the existing entry's value is returned.
-       Otherwise NULL is returned.  This allows callers to implement
-       chaining, etc.
-
-`lookup_hash`::
-
-       Lookup an entry in the hash table. If an entry with that hash exists
-       the entry's value is returned. Otherwise NULL is returned.
-
-`for_each_hash`::
-
-       Call a function for each entry in the hash table. The function is
-       expected to take the entry's value as its only argument and return an
-       int. If the function returns a negative int the loop is aborted
-       immediately.  Otherwise, the return value is accumulated and the sum
-       returned upon completion of the loop.
diff --git a/Documentation/technical/api-hashmap.txt b/Documentation/technical/api-hashmap.txt
new file mode 100644 (file)
index 0000000..42ca234
--- /dev/null
@@ -0,0 +1,235 @@
+hashmap API
+===========
+
+The hashmap API is a generic implementation of hash-based key-value mappings.
+
+Data Structures
+---------------
+
+`struct hashmap`::
+
+       The hash table structure.
++
+The `size` member keeps track of the total number of entries. The `cmpfn`
+member is a function used to compare two entries for equality. The `table` and
+`tablesize` members store the hash table and its size, respectively.
+
+`struct hashmap_entry`::
+
+       An opaque structure representing an entry in the hash table, which must
+       be used as first member of user data structures. Ideally it should be
+       followed by an int-sized member to prevent unused memory on 64-bit
+       systems due to alignment.
++
+The `hash` member is the entry's hash code and the `next` member points to the
+next entry in case of collisions (i.e. if multiple entries map to the same
+bucket).
+
+`struct hashmap_iter`::
+
+       An iterator structure, to be used with hashmap_iter_* functions.
+
+Types
+-----
+
+`int (*hashmap_cmp_fn)(const void *entry, const void *entry_or_key, const void *keydata)`::
+
+       User-supplied function to test two hashmap entries for equality. Shall
+       return 0 if the entries are equal.
++
+This function is always called with non-NULL `entry` / `entry_or_key`
+parameters that have the same hash code. When looking up an entry, the `key`
+and `keydata` parameters to hashmap_get and hashmap_remove are always passed
+as second and third argument, respectively. Otherwise, `keydata` is NULL.
+
+Functions
+---------
+
+`unsigned int strhash(const char *buf)`::
+`unsigned int strihash(const char *buf)`::
+`unsigned int memhash(const void *buf, size_t len)`::
+`unsigned int memihash(const void *buf, size_t len)`::
+
+       Ready-to-use hash functions for strings, using the FNV-1 algorithm (see
+       http://www.isthe.com/chongo/tech/comp/fnv).
++
+`strhash` and `strihash` take 0-terminated strings, while `memhash` and
+`memihash` operate on arbitrary-length memory.
++
+`strihash` and `memihash` are case insensitive versions.
+
+`void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function, size_t initial_size)`::
+
+       Initializes a hashmap structure.
++
+`map` is the hashmap to initialize.
++
+The `equals_function` can be specified to compare two entries for equality.
+If NULL, entries are considered equal if their hash codes are equal.
++
+If the total number of entries is known in advance, the `initial_size`
+parameter may be used to preallocate a sufficiently large table and thus
+prevent expensive resizing. If 0, the table is dynamically resized.
+
+`void hashmap_free(struct hashmap *map, int free_entries)`::
+
+       Frees a hashmap structure and allocated memory.
++
+`map` is the hashmap to free.
++
+If `free_entries` is true, each hashmap_entry in the map is freed as well
+(using stdlib's free()).
+
+`void hashmap_entry_init(void *entry, unsigned int hash)`::
+
+       Initializes a hashmap_entry structure.
++
+`entry` points to the entry to initialize.
++
+`hash` is the hash code of the entry.
+
+`void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata)`::
+
+       Returns the hashmap entry for the specified key, or NULL if not found.
++
+`map` is the hashmap structure.
++
+`key` is a hashmap_entry structure (or user data structure that starts with
+hashmap_entry) that has at least been initialized with the proper hash code
+(via `hashmap_entry_init`).
++
+If an entry with matching hash code is found, `key` and `keydata` are passed
+to `hashmap_cmp_fn` to decide whether the entry matches the key.
+
+`void *hashmap_get_next(const struct hashmap *map, const void *entry)`::
+
+       Returns the next equal hashmap entry, or NULL if not found. This can be
+       used to iterate over duplicate entries (see `hashmap_add`).
++
+`map` is the hashmap structure.
++
+`entry` is the hashmap_entry to start the search from, obtained via a previous
+call to `hashmap_get` or `hashmap_get_next`.
+
+`void hashmap_add(struct hashmap *map, void *entry)`::
+
+       Adds a hashmap entry. This allows to add duplicate entries (i.e.
+       separate values with the same key according to hashmap_cmp_fn).
++
+`map` is the hashmap structure.
++
+`entry` is the entry to add.
+
+`void *hashmap_put(struct hashmap *map, void *entry)`::
+
+       Adds or replaces a hashmap entry. If the hashmap contains duplicate
+       entries equal to the specified entry, only one of them will be replaced.
++
+`map` is the hashmap structure.
++
+`entry` is the entry to add or replace.
++
+Returns the replaced entry, or NULL if not found (i.e. the entry was added).
+
+`void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)`::
+
+       Removes a hashmap entry matching the specified key. If the hashmap
+       contains duplicate entries equal to the specified key, only one of
+       them will be removed.
++
+`map` is the hashmap structure.
++
+`key` is a hashmap_entry structure (or user data structure that starts with
+hashmap_entry) that has at least been initialized with the proper hash code
+(via `hashmap_entry_init`).
++
+If an entry with matching hash code is found, `key` and `keydata` are
+passed to `hashmap_cmp_fn` to decide whether the entry matches the key.
++
+Returns the removed entry, or NULL if not found.
+
+`void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)`::
+`void *hashmap_iter_next(struct hashmap_iter *iter)`::
+`void *hashmap_iter_first(struct hashmap *map, struct hashmap_iter *iter)`::
+
+       Used to iterate over all entries of a hashmap.
++
+`hashmap_iter_init` initializes a `hashmap_iter` structure.
++
+`hashmap_iter_next` returns the next hashmap_entry, or NULL if there are no
+more entries.
++
+`hashmap_iter_first` is a combination of both (i.e. initializes the iterator
+and returns the first entry, if any).
+
+Usage example
+-------------
+
+Here's a simple usage example that maps long keys to double values.
+[source,c]
+------------
+struct hashmap map;
+
+struct long2double {
+       struct hashmap_entry ent; /* must be the first member! */
+       long key;
+       double value;
+};
+
+static int long2double_cmp(const struct long2double *e1, const struct long2double *e2, const void *unused)
+{
+       return !(e1->key == e2->key);
+}
+
+void long2double_init(void)
+{
+       hashmap_init(&map, (hashmap_cmp_fn) long2double_cmp, 0);
+}
+
+void long2double_free(void)
+{
+       hashmap_free(&map, 1);
+}
+
+static struct long2double *find_entry(long key)
+{
+       struct long2double k;
+       hashmap_entry_init(&k, memhash(&key, sizeof(long)));
+       k.key = key;
+       return hashmap_get(&map, &k, NULL);
+}
+
+double get_value(long key)
+{
+       struct long2double *e = find_entry(key);
+       return e ? e->value : 0;
+}
+
+void set_value(long key, double value)
+{
+       struct long2double *e = find_entry(key);
+       if (!e) {
+               e = malloc(sizeof(struct long2double));
+               hashmap_entry_init(e, memhash(&key, sizeof(long)));
+               e->key = key;
+               hashmap_add(&map, e);
+       }
+       e->value = value;
+}
+------------
+
+Using variable-sized keys
+-------------------------
+
+The `hashmap_entry_get` and `hashmap_entry_remove` functions expect an ordinary
+`hashmap_entry` structure as key to find the correct entry. If the key data is
+variable-sized (e.g. a FLEX_ARRAY string) or quite large, it is undesirable
+to create a full-fledged entry structure on the heap and copy all the key data
+into the structure.
+
+In this case, the `keydata` parameter can be used to pass
+variable-sized key data directly to the comparison function, and the `key`
+parameter can be a stripped-down, fixed size entry structure allocated on the
+stack.
+
+See test-hashmap.c for an example using arbitrary-length strings as keys.
diff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt
new file mode 100644 (file)
index 0000000..f8c18a0
--- /dev/null
@@ -0,0 +1,164 @@
+GIT bitmap v1 format
+====================
+
+       - A header appears at the beginning:
+
+               4-byte signature: {'B', 'I', 'T', 'M'}
+
+               2-byte version number (network byte order)
+                       The current implementation only supports version 1
+                       of the bitmap index (the same one as JGit).
+
+               2-byte flags (network byte order)
+
+                       The following flags are supported:
+
+                       - BITMAP_OPT_FULL_DAG (0x1) REQUIRED
+                       This flag must always be present. It implies that the bitmap
+                       index has been generated for a packfile with full closure
+                       (i.e. where every single object in the packfile can find
+                        its parent links inside the same packfile). This is a
+                       requirement for the bitmap index format, also present in JGit,
+                       that greatly reduces the complexity of the implementation.
+
+                       - BITMAP_OPT_HASH_CACHE (0x4)
+                       If present, the end of the bitmap file contains
+                       `N` 32-bit name-hash values, one per object in the
+                       pack. The format and meaning of the name-hash is
+                       described below.
+
+               4-byte entry count (network byte order)
+
+                       The total count of entries (bitmapped commits) in this bitmap index.
+
+               20-byte checksum
+
+                       The SHA1 checksum of the pack this bitmap index belongs to.
+
+       - 4 EWAH bitmaps that act as type indexes
+
+               Type indexes are serialized after the hash cache in the shape
+               of four EWAH bitmaps stored consecutively (see Appendix A for
+               the serialization format of an EWAH bitmap).
+
+               There is a bitmap for each Git object type, stored in the following
+               order:
+
+                       - Commits
+                       - Trees
+                       - Blobs
+                       - Tags
+
+               In each bitmap, the `n`th bit is set to true if the `n`th object
+               in the packfile is of that type.
+
+               The obvious consequence is that the OR of all 4 bitmaps will result
+               in a full set (all bits set), and the AND of all 4 bitmaps will
+               result in an empty bitmap (no bits set).
+
+       - N entries with compressed bitmaps, one for each indexed commit
+
+               Where `N` is the total amount of entries in this bitmap index.
+               Each entry contains the following:
+
+               - 4-byte object position (network byte order)
+                       The position **in the index for the packfile** where the
+                       bitmap for this commit is found.
+
+               - 1-byte XOR-offset
+                       The xor offset used to compress this bitmap. For an entry
+                       in position `x`, a XOR offset of `y` means that the actual
+                       bitmap representing this commit is composed by XORing the
+                       bitmap for this entry with the bitmap in entry `x-y` (i.e.
+                       the bitmap `y` entries before this one).
+
+                       Note that this compression can be recursive. In order to
+                       XOR this entry with a previous one, the previous entry needs
+                       to be decompressed first, and so on.
+
+                       The hard-limit for this offset is 160 (an entry can only be
+                       xor'ed against one of the 160 entries preceding it). This
+                       number is always positive, and hence entries are always xor'ed
+                       with **previous** bitmaps, not bitmaps that will come afterwards
+                       in the index.
+
+               - 1-byte flags for this bitmap
+                       At the moment the only available flag is `0x1`, which hints
+                       that this bitmap can be re-used when rebuilding bitmap indexes
+                       for the repository.
+
+               - The compressed bitmap itself, see Appendix A.
+
+== Appendix A: Serialization format for an EWAH bitmap
+
+Ewah bitmaps are serialized in the same protocol as the JAVAEWAH
+library, making them backwards compatible with the JGit
+implementation:
+
+       - 4-byte number of bits of the resulting UNCOMPRESSED bitmap
+
+       - 4-byte number of words of the COMPRESSED bitmap, when stored
+
+       - N x 8-byte words, as specified by the previous field
+
+               This is the actual content of the compressed bitmap.
+
+       - 4-byte position of the current RLW for the compressed
+               bitmap
+
+All words are stored in network byte order for their corresponding
+sizes.
+
+The compressed bitmap is stored in a form of run-length encoding, as
+follows.  It consists of a concatenation of an arbitrary number of
+chunks.  Each chunk consists of one or more 64-bit words
+
+     H  L_1  L_2  L_3 .... L_M
+
+H is called RLW (run length word).  It consists of (from lower to higher
+order bits):
+
+     - 1 bit: the repeated bit B
+
+     - 32 bits: repetition count K (unsigned)
+
+     - 31 bits: literal word count M (unsigned)
+
+The bitstream represented by the above chunk is then:
+
+     - K repetitions of B
+
+     - The bits stored in `L_1` through `L_M`.  Within a word, bits at
+       lower order come earlier in the stream than those at higher
+       order.
+
+The next word after `L_M` (if any) must again be a RLW, for the next
+chunk.  For efficient appending to the bitstream, the EWAH stores a
+pointer to the last RLW in the stream.
+
+
+== Appendix B: Optional Bitmap Sections
+
+These sections may or may not be present in the `.bitmap` file; their
+presence is indicated by the header flags section described above.
+
+Name-hash cache
+---------------
+
+If the BITMAP_OPT_HASH_CACHE flag is set, the end of the bitmap contains
+a cache of 32-bit values, one per object in the pack. The value at
+position `i` is the hash of the pathname at which the `i`th object
+(counting in index order) in the pack can be found.  This can be fed
+into the delta heuristics to compare objects with similar pathnames.
+
+The hash algorithm used is:
+
+    hash = 0;
+    while ((c = *name++))
+           if (!isspace(c))
+                   hash = (hash >> 2) + (c << 24);
+
+Note that this hashing scheme is tied to the BITMAP_OPT_HASH_CACHE flag.
+If implementations want to choose a different hashing scheme, they are
+free to do so, but MUST allocate a new header flag (because comparing
+hashes made under two different schemes would be pointless).
index c73b62f5e1ced76d783dcd5e7aef64016ca1fb4c..39c64105a61a3743da0c8c3b04ee31106090cfa7 100644 (file)
@@ -338,7 +338,8 @@ during a prior round.  This helps to ensure that at least one common
 ancestor is found before we give up entirely.
 
 Once the 'done' line is read from the client, the server will either
-send a final 'ACK obj-id' or it will send a 'NAK'. The server only sends
+send a final 'ACK obj-id' or it will send a 'NAK'. 'obj-id' is the object
+name of the last commit determined to be common. The server only sends
 ACK after 'done' if there is at least one common base and multi_ack or
 multi_ack_detailed is enabled. The server always sends NAK after 'done'
 if there is no common base found.
index e3e792476e7a6b7554582469b2c5ac172b2f17dd..e17434384724662aabb60d7fceed40a433207608 100644 (file)
@@ -69,6 +69,24 @@ ends.
 Without multi_ack the client would have sent that c-b-a chain anyway,
 interleaved with S-R-Q.
 
+multi_ack_detailed
+------------------
+This is an extension of multi_ack that permits client to better
+understand the server's in-memory state. See pack-protocol.txt,
+section "Packfile Negotiation" for more information.
+
+no-done
+-------
+This capability should only be used with the smart HTTP protocol. If
+multi_ack_detailed and no-done are both present, then the sender is
+free to immediately send a pack following its first "ACK obj-id ready"
+message.
+
+Without no-done in the smart HTTP protocol, the server session would
+end and the client has to make another trip to send "done" before
+the server can send the pack. no-done removes the last round and
+thus slightly reduces latency.
+
 thin-pack
 ---------
 
index dddaf4f287cf5cd5e99ad2587d53ba7582c51e29..d4ce53a71b3ffd3b2a9d115279880d3e1aeb03a6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -555,6 +555,7 @@ TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 TEST_PROGRAMS_NEED_X += test-genrandom
+TEST_PROGRAMS_NEED_X += test-hashmap
 TEST_PROGRAMS_NEED_X += test-index-version
 TEST_PROGRAMS_NEED_X += test-line-buffer
 TEST_PROGRAMS_NEED_X += test-match-trees
@@ -663,6 +664,8 @@ LIB_H += diff.h
 LIB_H += diffcore.h
 LIB_H += dir.h
 LIB_H += exec_cmd.h
+LIB_H += ewah/ewok.h
+LIB_H += ewah/ewok_rlw.h
 LIB_H += fetch-pack.h
 LIB_H += fmt-merge-msg.h
 LIB_H += fsck.h
@@ -671,7 +674,7 @@ LIB_H += git-compat-util.h
 LIB_H += gpg-interface.h
 LIB_H += graph.h
 LIB_H += grep.h
-LIB_H += hash.h
+LIB_H += hashmap.h
 LIB_H += help.h
 LIB_H += http.h
 LIB_H += kwset.h
@@ -690,8 +693,10 @@ LIB_H += notes-merge.h
 LIB_H += notes-utils.h
 LIB_H += notes.h
 LIB_H += object.h
+LIB_H += pack-objects.h
 LIB_H += pack-revindex.h
 LIB_H += pack.h
+LIB_H += pack-bitmap.h
 LIB_H += parse-options.h
 LIB_H += patch-ids.h
 LIB_H += pathspec.h
@@ -795,6 +800,10 @@ LIB_OBJS += dir.o
 LIB_OBJS += editor.o
 LIB_OBJS += entry.o
 LIB_OBJS += environment.o
+LIB_OBJS += ewah/bitmap.o
+LIB_OBJS += ewah/ewah_bitmap.o
+LIB_OBJS += ewah/ewah_io.o
+LIB_OBJS += ewah/ewah_rlw.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fetch-pack.o
 LIB_OBJS += fsck.o
@@ -802,7 +811,7 @@ LIB_OBJS += gettext.o
 LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
-LIB_OBJS += hash.o
+LIB_OBJS += hashmap.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
 LIB_OBJS += ident.o
@@ -826,7 +835,10 @@ LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
 LIB_OBJS += object.o
+LIB_OBJS += pack-bitmap.o
+LIB_OBJS += pack-bitmap-write.o
 LIB_OBJS += pack-check.o
+LIB_OBJS += pack-objects.o
 LIB_OBJS += pack-revindex.o
 LIB_OBJS += pack-write.o
 LIB_OBJS += pager.o
@@ -2479,8 +2491,9 @@ profile-clean:
        $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
 
 clean: profile-clean coverage-clean
-       $(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
-               builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
+       $(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o
+       $(RM) xdiff/*.o vcs-svn/*.o ewah/*.o builtin/*.o
+       $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
index bb52cf9476df53ddb9788f16082e2e92e76a4db5..e50885caa1043665dfe2d995e0a2f7a7a7bb51ca 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.9.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.0.0.txt
\ No newline at end of file
index 3eca9f5ffdd6e3596584ed0aa79e2634683bd496..486f823c78118212080a48e74debd7adae241cda 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -2,7 +2,6 @@
 
 int advice_push_update_rejected = 1;
 int advice_push_non_ff_current = 1;
-int advice_push_non_ff_default = 1;
 int advice_push_non_ff_matching = 1;
 int advice_push_already_exists = 1;
 int advice_push_fetch_first = 1;
@@ -23,7 +22,6 @@ static struct {
 } advice_config[] = {
        { "pushupdaterejected", &advice_push_update_rejected },
        { "pushnonffcurrent", &advice_push_non_ff_current },
-       { "pushnonffdefault", &advice_push_non_ff_default },
        { "pushnonffmatching", &advice_push_non_ff_matching },
        { "pushalreadyexists", &advice_push_already_exists },
        { "pushfetchfirst", &advice_push_fetch_first },
index 08fbc8ee3ce79f265bbe15b63e2ae561ee8a0857..5ecc6c154e5a7207d18bbb05da5e35b2b4eae230 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -5,7 +5,6 @@
 
 extern int advice_push_update_rejected;
 extern int advice_push_non_ff_current;
-extern int advice_push_non_ff_default;
 extern int advice_push_non_ff_matching;
 extern int advice_push_already_exists;
 extern int advice_push_fetch_first;
index 37200b41f1dce3359c491039aeebea0108597833..8448d27877e3d77fcae01b1354d192121e050528 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -685,7 +685,6 @@ static void mark_expected_rev(char *bisect_rev_hex)
 
 static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
 {
-       int res;
 
        mark_expected_rev(bisect_rev_hex);
 
@@ -696,6 +695,7 @@ static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
                        die("update-ref --no-deref HEAD failed on %s",
                            bisect_rev_hex);
        } else {
+               int res;
                res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
                if (res)
                        exit(res);
index e1a1eb609754a745da956a6994d23aec3c4c9c05..22b125cf8c122e3a1716f34778fa5595454fe76a 100644 (file)
   #define setW(x, val) (W(x) = (val))
 #endif
 
-/*
- * Performance might be improved if the CPU architecture is OK with
- * unaligned 32-bit loads and a fast ntohl() is available.
- * Otherwise fall back to byte loads and shifts which is portable,
- * and is faster on architectures with memory alignment issues.
- */
-
-#if defined(__i386__) || defined(__x86_64__) || \
-    defined(_M_IX86) || defined(_M_X64) || \
-    defined(__ppc__) || defined(__ppc64__) || \
-    defined(__powerpc__) || defined(__powerpc64__) || \
-    defined(__s390__) || defined(__s390x__)
-
-#define get_be32(p)    ntohl(*(unsigned int *)(p))
-#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
-
-#else
-
-#define get_be32(p)    ( \
-       (*((unsigned char *)(p) + 0) << 24) | \
-       (*((unsigned char *)(p) + 1) << 16) | \
-       (*((unsigned char *)(p) + 2) <<  8) | \
-       (*((unsigned char *)(p) + 3) <<  0) )
-#define put_be32(p, v) do { \
-       unsigned int __v = (v); \
-       *((unsigned char *)(p) + 0) = __v >> 24; \
-       *((unsigned char *)(p) + 1) = __v >> 16; \
-       *((unsigned char *)(p) + 2) = __v >>  8; \
-       *((unsigned char *)(p) + 3) = __v >>  0; } while (0)
-
-#endif
-
 /* This "rolls" over the 512-bit array */
 #define W(x) (array[(x)&15])
 
index 2a2722fa10aa981695b21984e7c425c35b4f18d0..4b045bace12c1fa951785dc09c05a46b09cd9fc7 100644 (file)
@@ -26,55 +26,8 @@ static int take_worktree_changes;
 struct update_callback_data {
        int flags;
        int add_errors;
-       const char *implicit_dot;
-       size_t implicit_dot_len;
-
-       /* only needed for 2.0 transition preparation */
-       int warn_add_would_remove;
 };
 
-static const char *option_with_implicit_dot;
-static const char *short_option_with_implicit_dot;
-
-static void warn_pathless_add(void)
-{
-       static int shown;
-       assert(option_with_implicit_dot && short_option_with_implicit_dot);
-
-       if (shown)
-               return;
-       shown = 1;
-
-       /*
-        * To be consistent with "git add -p" and most Git
-        * commands, we should default to being tree-wide, but
-        * this is not the original behavior and can't be
-        * changed until users trained themselves not to type
-        * "git add -u" or "git add -A". For now, we warn and
-        * keep the old behavior. Later, the behavior can be changed
-        * to tree-wide, keeping the warning for a while, and
-        * eventually we can drop the warning.
-        */
-       warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n"
-                 "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n"
-                 "To add content for the whole tree, run:\n"
-                 "\n"
-                 "  git add %s :/\n"
-                 "  (or git add %s :/)\n"
-                 "\n"
-                 "To restrict the command to the current directory, run:\n"
-                 "\n"
-                 "  git add %s .\n"
-                 "  (or git add %s .)\n"
-                 "\n"
-                 "With the current Git version, the command is restricted to "
-                 "the current directory.\n"
-                 ""),
-               option_with_implicit_dot, short_option_with_implicit_dot,
-               option_with_implicit_dot, short_option_with_implicit_dot,
-               option_with_implicit_dot, short_option_with_implicit_dot);
-}
-
 static int fix_unmerged_status(struct diff_filepair *p,
                               struct update_callback_data *data)
 {
@@ -96,49 +49,15 @@ static int fix_unmerged_status(struct diff_filepair *p,
                return DIFF_STATUS_MODIFIED;
 }
 
-static const char *add_would_remove_warning = N_(
-       "You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
-"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
-"Paths like '%s' that are\n"
-"removed from your working tree are ignored with this version of Git.\n"
-"\n"
-"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
-"  ignores paths you removed from your working tree.\n"
-"\n"
-"* 'git add --all <pathspec>' will let you also record the removals.\n"
-"\n"
-"Run 'git status' to check the paths you removed from your working tree.\n");
-
-static void warn_add_would_remove(const char *path)
-{
-       warning(_(add_would_remove_warning), path);
-}
-
 static void update_callback(struct diff_queue_struct *q,
                            struct diff_options *opt, void *cbdata)
 {
        int i;
        struct update_callback_data *data = cbdata;
-       const char *implicit_dot = data->implicit_dot;
-       size_t implicit_dot_len = data->implicit_dot_len;
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                const char *path = p->one->path;
-               /*
-                * Check if "git add -A" or "git add -u" was run from a
-                * subdirectory with a modified file outside that directory,
-                * and warn if so.
-                *
-                * "git add -u" will behave like "git add -u :/" instead of
-                * "git add -u ." in the future.  This warning prepares for
-                * that change.
-                */
-               if (implicit_dot &&
-                   strncmp_icase(path, implicit_dot, implicit_dot_len)) {
-                       warn_pathless_add();
-                       continue;
-               }
                switch (fix_unmerged_status(p, data)) {
                default:
                        die(_("unexpected diff status %c"), p->status);
@@ -151,10 +70,6 @@ static void update_callback(struct diff_queue_struct *q,
                        }
                        break;
                case DIFF_STATUS_DELETED:
-                       if (data->warn_add_would_remove) {
-                               warn_add_would_remove(path);
-                               data->warn_add_would_remove = 0;
-                       }
                        if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
                                break;
                        if (!(data->flags & ADD_CACHE_PRETEND))
@@ -166,37 +81,28 @@ static void update_callback(struct diff_queue_struct *q,
        }
 }
 
-static void update_files_in_cache(const char *prefix,
-                                 const struct pathspec *pathspec,
-                                 struct update_callback_data *data)
+int add_files_to_cache(const char *prefix,
+                      const struct pathspec *pathspec, int flags)
 {
+       struct update_callback_data data;
        struct rev_info rev;
 
+       memset(&data, 0, sizeof(data));
+       data.flags = flags;
+
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
        if (pathspec)
                copy_pathspec(&rev.prune_data, pathspec);
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
-       rev.diffopt.format_callback_data = data;
+       rev.diffopt.format_callback_data = &data;
        rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-}
-
-int add_files_to_cache(const char *prefix,
-                      const struct pathspec *pathspec, int flags)
-{
-       struct update_callback_data data;
-
-       memset(&data, 0, sizeof(data));
-       data.flags = flags;
-       update_files_in_cache(prefix, pathspec, &data);
        return !!data.add_errors;
 }
 
-#define WARN_IMPLICIT_DOT (1u << 0)
-static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
-                            int prefix, unsigned flag)
+static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix)
 {
        char *seen;
        int i;
@@ -208,19 +114,8 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
        i = dir->nr;
        while (--i >= 0) {
                struct dir_entry *entry = *src++;
-               if (match_pathspec_depth(pathspec, entry->name, entry->len,
-                                        prefix, seen))
+               if (dir_path_match(entry, pathspec, prefix, seen))
                        *dst++ = entry;
-               else if (flag & WARN_IMPLICIT_DOT)
-                       /*
-                        * "git add -A" was run from a subdirectory with a
-                        * new file outside that directory.
-                        *
-                        * "git add -A" will behave like "git add -A :/"
-                        * instead of "git add -A ." in the future.
-                        * Warn about the coming behavior change.
-                        */
-                       warn_pathless_add();
        }
        dir->nr = dst - dir->entries;
        add_pathspec_matches_against_index(pathspec, seen);
@@ -339,7 +234,7 @@ N_("The following paths are ignored by one of your .gitignore files:\n");
 static int verbose, show_only, ignored_too, refresh_only;
 static int ignore_add_errors, intent_to_add, ignore_missing;
 
-#define ADDREMOVE_DEFAULT 0 /* Change to 1 in Git 2.0 */
+#define ADDREMOVE_DEFAULT 1
 static int addremove = ADDREMOVE_DEFAULT;
 static int addremove_explicit = -1; /* unspecified */
 
@@ -412,8 +307,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        int add_new_files;
        int require_pathspec;
        char *seen = NULL;
-       int implicit_dot = 0;
-       struct update_callback_data update_data;
 
        git_config(add_config, NULL);
 
@@ -437,36 +330,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        if (addremove && take_worktree_changes)
                die(_("-A and -u are mutually incompatible"));
 
-       /*
-        * Warn when "git add pathspec..." was given without "-u" or "-A"
-        * and pathspec... covers a removed path.
-        */
-       memset(&update_data, 0, sizeof(update_data));
-       if (!take_worktree_changes && addremove_explicit < 0)
-               update_data.warn_add_would_remove = 1;
-
        if (!take_worktree_changes && addremove_explicit < 0 && argc)
-               /*
-                * Turn "git add pathspec..." to "git add -A pathspec..."
-                * in Git 2.0 but not yet
-                */
-               ; /* addremove = 1; */
+               /* Turn "git add pathspec..." to "git add -A pathspec..." */
+               addremove = 1;
 
        if (!show_only && ignore_missing)
                die(_("Option --ignore-missing can only be used together with --dry-run"));
-       if (addremove) {
-               option_with_implicit_dot = "--all";
-               short_option_with_implicit_dot = "-A";
-       }
-       if (take_worktree_changes) {
-               option_with_implicit_dot = "--update";
-               short_option_with_implicit_dot = "-u";
-       }
-       if (option_with_implicit_dot && !argc) {
-               static const char *here[2] = { ".", NULL };
+
+       if ((0 < addremove_explicit || take_worktree_changes) && !argc) {
+               static const char *whole[2] = { ":/", NULL };
                argc = 1;
-               argv = here;
-               implicit_dot = 1;
+               argv = whole;
        }
 
        add_new_files = !take_worktree_changes && !refresh_only;
@@ -479,8 +353,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                 (intent_to_add ? ADD_CACHE_INTENT : 0) |
                 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
                 (!(addremove || take_worktree_changes)
-                 ? ADD_CACHE_IGNORE_REMOVAL : 0)) |
-                (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0);
+                 ? ADD_CACHE_IGNORE_REMOVAL : 0));
 
        if (require_pathspec && argc == 0) {
                fprintf(stderr, _("Nothing specified, nothing added.\n"));
@@ -514,18 +387,15 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
                memset(&empty_pathspec, 0, sizeof(empty_pathspec));
                /* This picks up the paths that are not tracked */
-               baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec);
+               baselen = fill_directory(&dir, &pathspec);
                if (pathspec.nr)
-                       seen = prune_directory(&dir, &pathspec, baselen,
-                                       implicit_dot ? WARN_IMPLICIT_DOT : 0);
+                       seen = prune_directory(&dir, &pathspec, baselen);
        }
 
        if (refresh_only) {
                refresh(verbose, &pathspec);
                goto finish;
        }
-       if (implicit_dot && prefix)
-               refresh_cache(REFRESH_QUIET);
 
        if (pathspec.nr) {
                int i;
@@ -565,21 +435,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        plug_bulk_checkin();
 
-       if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) {
-               /*
-                * Check for modified files throughout the worktree so
-                * update_callback has a chance to warn about changes
-                * outside the cwd.
-                */
-               update_data.implicit_dot = prefix;
-               update_data.implicit_dot_len = strlen(prefix);
-               free_pathspec(&pathspec);
-               memset(&pathspec, 0, sizeof(pathspec));
-       }
-       update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
-       update_files_in_cache(prefix, &pathspec, &update_data);
+       exit_status |= add_files_to_cache(prefix, &pathspec, flags);
 
-       exit_status |= !!update_data.add_errors;
        if (add_new_files)
                exit_status |= add_files(&dir, flags);
 
index b0d0986226ccb7fa06b08bacba6269b8834e8dd6..a7e72d57ab0d23ef3becb3fd7d4564d6050efe6e 100644 (file)
@@ -1943,13 +1943,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                                       size - offset - hdrsize, patch);
 
        if (!patchsize) {
-               static const char *binhdr[] = {
-                       "Binary files ",
-                       "Files ",
-                       NULL,
-               };
                static const char git_binary[] = "GIT binary patch\n";
-               int i;
                int hd = hdrsize + offset;
                unsigned long llen = linelen(buffer + hd, size - hd);
 
@@ -1965,6 +1959,12 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                                patchsize = 0;
                }
                else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
+                       static const char *binhdr[] = {
+                               "Binary files ",
+                               "Files ",
+                               NULL,
+                       };
+                       int i;
                        for (i = 0; binhdr[i]; i++) {
                                int len = strlen(binhdr[i]);
                                if (len < size - hd &&
index e44a6bb30a5a5299c6d57150dd2f23454b25891a..e5b5d71bad8653f4b9d19044c3c36dc2ab8b42f1 100644 (file)
@@ -197,7 +197,6 @@ static void drop_origin_blob(struct origin *o)
  * scoreboard structure, sorted by the target line number.
  */
 struct blame_entry {
-       struct blame_entry *prev;
        struct blame_entry *next;
 
        /* the first line of this group in the final image;
@@ -256,15 +255,6 @@ struct scoreboard {
        int *lineno;
 };
 
-static inline int same_suspect(struct origin *a, struct origin *b)
-{
-       if (a == b)
-               return 1;
-       if (a->commit != b->commit)
-               return 0;
-       return !strcmp(a->path, b->path);
-}
-
 static void sanity_check_refcnt(struct scoreboard *);
 
 /*
@@ -277,13 +267,11 @@ static void coalesce(struct scoreboard *sb)
        struct blame_entry *ent, *next;
 
        for (ent = sb->ent; ent && (next = ent->next); ent = next) {
-               if (same_suspect(ent->suspect, next->suspect) &&
+               if (ent->suspect == next->suspect &&
                    ent->guilty == next->guilty &&
                    ent->s_lno + ent->num_lines == next->s_lno) {
                        ent->num_lines += next->num_lines;
                        ent->next = next->next;
-                       if (ent->next)
-                               ent->next->prev = ent;
                        origin_decref(next->suspect);
                        free(next);
                        ent->score = 0;
@@ -534,7 +522,7 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
                prev = ent;
 
        /* prev, if not NULL, is the last one that is below e */
-       e->prev = prev;
+
        if (prev) {
                e->next = prev->next;
                prev->next = e;
@@ -543,8 +531,6 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
                e->next = sb->ent;
                sb->ent = e;
        }
-       if (e->next)
-               e->next->prev = e;
 }
 
 /*
@@ -555,14 +541,12 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
  */
 static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
 {
-       struct blame_entry *p, *n;
+       struct blame_entry *n;
 
-       p = dst->prev;
        n = dst->next;
        origin_incref(src->suspect);
        origin_decref(dst->suspect);
        memcpy(dst, src, sizeof(*src));
-       dst->prev = p;
        dst->next = n;
        dst->score = 0;
 }
@@ -742,7 +726,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target)
        int last_in_target = -1;
 
        for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || !same_suspect(e->suspect, target))
+               if (e->guilty || e->suspect != target)
                        continue;
                if (last_in_target < e->s_lno + e->num_lines)
                        last_in_target = e->s_lno + e->num_lines;
@@ -762,7 +746,7 @@ static void blame_chunk(struct scoreboard *sb,
        struct blame_entry *e;
 
        for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || !same_suspect(e->suspect, target))
+               if (e->guilty || e->suspect != target)
                        continue;
                if (same <= e->s_lno)
                        continue;
@@ -939,7 +923,6 @@ static void find_copy_in_blob(struct scoreboard *sb,
                              mmfile_t *file_p)
 {
        const char *cp;
-       int cnt;
        mmfile_t file_o;
        struct handle_split_cb_data d;
 
@@ -950,13 +933,7 @@ static void find_copy_in_blob(struct scoreboard *sb,
         */
        cp = nth_line(sb, ent->lno);
        file_o.ptr = (char *) cp;
-       cnt = ent->num_lines;
-
-       while (cnt && cp < sb->final_buf + sb->final_buf_size) {
-               if (*cp++ == '\n')
-                       cnt--;
-       }
-       file_o.size = cp - file_o.ptr;
+       file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;
 
        /*
         * file_o is a part of final image we are annotating.
@@ -992,7 +969,7 @@ static int find_move_in_parent(struct scoreboard *sb,
        while (made_progress) {
                made_progress = 0;
                for (e = sb->ent; e; e = e->next) {
-                       if (e->guilty || !same_suspect(e->suspect, target) ||
+                       if (e->guilty || e->suspect != target ||
                            ent_score(sb, e) < blame_move_score)
                                continue;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
@@ -1027,14 +1004,14 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb,
 
        for (e = sb->ent, num_ents = 0; e; e = e->next)
                if (!e->scanned && !e->guilty &&
-                   same_suspect(e->suspect, target) &&
+                   e->suspect == target &&
                    min_score < ent_score(sb, e))
                        num_ents++;
        if (num_ents) {
                blame_list = xcalloc(num_ents, sizeof(struct blame_list));
                for (e = sb->ent, i = 0; e; e = e->next)
                        if (!e->scanned && !e->guilty &&
-                           same_suspect(e->suspect, target) &&
+                           e->suspect == target &&
                            min_score < ent_score(sb, e))
                                blame_list[i++].ent = e;
        }
@@ -1178,7 +1155,7 @@ static void pass_whole_blame(struct scoreboard *sb,
                origin->file.ptr = NULL;
        }
        for (e = sb->ent; e; e = e->next) {
-               if (!same_suspect(e->suspect, origin))
+               if (e->suspect != origin)
                        continue;
                origin_incref(porigin);
                origin_decref(e->suspect);
@@ -1567,7 +1544,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 
                /* Take responsibility for the remaining entries */
                for (ent = sb->ent; ent; ent = ent->next)
-                       if (same_suspect(ent->suspect, suspect))
+                       if (ent->suspect == suspect)
                                found_guilty_entry(ent);
                origin_decref(suspect);
 
@@ -1580,14 +1557,14 @@ static const char *format_time(unsigned long time, const char *tz_str,
                               int show_raw_time)
 {
        static char time_buf[128];
-       const char *time_str;
-       int time_len;
-       int tz;
 
        if (show_raw_time) {
                snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
        }
        else {
+               const char *time_str;
+               int time_len;
+               int tz;
                tz = atoi(tz_str);
                time_str = show_date(time, tz, blame_date_mode);
                time_len = strlen(time_str);
@@ -1772,25 +1749,41 @@ static int prepare_lines(struct scoreboard *sb)
 {
        const char *buf = sb->final_buf;
        unsigned long len = sb->final_buf_size;
-       int num = 0, incomplete = 0, bol = 1;
+       const char *end = buf + len;
+       const char *p;
+       int *lineno;
+       int num = 0, incomplete = 0;
 
-       if (len && buf[len-1] != '\n')
-               incomplete++; /* incomplete line at the end */
-       while (len--) {
-               if (bol) {
-                       sb->lineno = xrealloc(sb->lineno,
-                                             sizeof(int *) * (num + 1));
-                       sb->lineno[num] = buf - sb->final_buf;
-                       bol = 0;
-               }
-               if (*buf++ == '\n') {
+       for (p = buf;;) {
+               p = memchr(p, '\n', end - p);
+               if (p) {
+                       p++;
                        num++;
-                       bol = 1;
+                       continue;
                }
+               break;
        }
-       sb->lineno = xrealloc(sb->lineno,
-                             sizeof(int *) * (num + incomplete + 1));
-       sb->lineno[num + incomplete] = buf - sb->final_buf;
+
+       if (len && end[-1] != '\n')
+               incomplete++; /* incomplete line at the end */
+
+       sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1));
+       lineno = sb->lineno;
+
+       *lineno++ = 0;
+       for (p = buf;;) {
+               p = memchr(p, '\n', end - p);
+               if (p) {
+                       p++;
+                       *lineno++ = p - buf;
+                       continue;
+               }
+               break;
+       }
+
+       if (incomplete)
+               *lineno++ = len;
+
        sb->num_lines = num + incomplete;
        return sb->num_lines;
 }
@@ -2502,8 +2495,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                ent->suspect = o;
                ent->s_lno = bottom;
                ent->next = next;
-               if (next)
-                       next->prev = ent;
                origin_incref(o);
        }
        origin_decref(o);
index e9af7b2bfb932d75cdface880be9814885068a50..5600ec3f6165ae04682f30481142452438f856af 100644 (file)
@@ -102,6 +102,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
        struct git_attr_check *check;
        int cnt, i, doubledash, filei;
 
+       if (!is_bare_repository())
+               setup_work_tree();
+
        git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, check_attr_options,
index 5df3837e3102e2f7d432ec11bb4685535bfaa0ac..ada51fa70ff59b10fd79168a4377d923638591e9 100644 (file)
@@ -297,8 +297,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                 * match_pathspec() for _all_ entries when
                 * opts->source_tree != NULL.
                 */
-               if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce),
-                                  0, ps_matched))
+               if (ce_path_match(ce, &opts->pathspec, ps_matched))
                        ce->ce_flags |= CE_MATCHED;
        }
 
index 2f26297142fde89ec842d4e01665b3ebb24a9b59..114d7bf879690fb1c1c33f768b86776b6ff60ef8 100644 (file)
@@ -154,7 +154,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
        DIR *dir;
        struct strbuf quoted = STRBUF_INIT;
        struct dirent *e;
-       int res = 0, ret = 0, gone = 1, original_len = path->len, len, i;
+       int res = 0, ret = 0, gone = 1, original_len = path->len, len;
        unsigned char submodule_head[20];
        struct string_list dels = STRING_LIST_INIT_DUP;
 
@@ -242,6 +242,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
        }
 
        if (!*dir_gone && !quiet) {
+               int i;
                for (i = 0; i < dels.nr; i++)
                        printf(dry_run ?  _(msg_would_remove) : _(msg_remove), dels.items[i].string);
        }
@@ -933,36 +934,18 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
-               int len, pos;
                int matches = 0;
-               const struct cache_entry *ce;
                struct stat st;
                const char *rel;
 
-               /*
-                * Remove the '/' at the end that directory
-                * walking adds for directory entries.
-                */
-               len = ent->len;
-               if (len && ent->name[len-1] == '/')
-                       len--;
-               pos = cache_name_pos(ent->name, len);
-               if (0 <= pos)
-                       continue;       /* exact match */
-               pos = -pos - 1;
-               if (pos < active_nr) {
-                       ce = active_cache[pos];
-                       if (ce_namelen(ce) == len &&
-                           !memcmp(ce->name, ent->name, len))
-                               continue; /* Yup, this one exists unmerged */
-               }
+               if (!cache_name_is_other(ent->name, ent->len))
+                       continue;
 
                if (lstat(ent->name, &st))
                        die_errno("Cannot lstat '%s'", ent->name);
 
                if (pathspec.nr)
-                       matches = match_pathspec_depth(&pathspec, ent->name,
-                                                      len, 0, NULL);
+                       matches = dir_path_match(ent, &pathspec, 0, NULL);
 
                if (S_ISDIR(st.st_mode)) {
                        if (remove_directories || (matches == MATCHED_EXACTLY)) {
index f641ff2a898cf76d288ed139772e247015ca554b..987a4c3d731d7cf01f61e4fddec208b4cb51a4af 100644 (file)
@@ -12,6 +12,8 @@
 
 static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1> <changelog";
 
+static const char *sign_commit;
+
 static void new_parent(struct commit *parent, struct commit_list **parents_p)
 {
        unsigned char *sha1 = parent->object.sha1;
@@ -31,6 +33,10 @@ static int commit_tree_config(const char *var, const char *value, void *cb)
        int status = git_gpg_config(var, value, NULL);
        if (status)
                return status;
+       if (!strcmp(var, "commit.gpgsign")) {
+               sign_commit = git_config_bool(var, value) ? "" : NULL;
+               return 0;
+       }
        return git_default_config(var, value, cb);
 }
 
@@ -41,7 +47,6 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        unsigned char tree_sha1[20];
        unsigned char commit_sha1[20];
        struct strbuf buffer = STRBUF_INIT;
-       const char *sign_commit = NULL;
 
        git_config(commit_tree_config, NULL);
 
@@ -66,6 +71,11 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
+               if (!strcmp(arg, "--no-gpg-sign")) {
+                       sign_commit = NULL;
+                       continue;
+               }
+
                if (!strcmp(arg, "-m")) {
                        if (argc <= ++i)
                                usage(commit_tree_usage);
index 3767478c6ddb02cf8a4a418f8911102aeaacfbe8..3783bcadcd5232ceccab7f4fd2559a9999c53ffa 100644 (file)
@@ -234,7 +234,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
 
                if (ce->ce_flags & CE_UPDATE)
                        continue;
-               if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
+               if (!ce_path_match(ce, pattern, m))
                        continue;
                item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
@@ -307,7 +307,6 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
        int fd;
        struct string_list partial;
        struct pathspec pathspec;
-       char *old_index_env = NULL;
        int refresh_flags = REFRESH_QUIET;
 
        if (is_status)
@@ -320,6 +319,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
                die(_("index file corrupt"));
 
        if (interactive) {
+               char *old_index_env = NULL;
                fd = hold_locked_index(&index_lock, 1);
 
                refresh_cache_or_die(refresh_flags);
@@ -600,12 +600,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 {
        struct stat statbuf;
        struct strbuf committer_ident = STRBUF_INIT;
-       int commitable, saved_color_setting;
+       int commitable;
        struct strbuf sb = STRBUF_INIT;
-       char *buffer;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
-       int ident_shown = 0;
        int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
        int old_display_comment_prefix;
 
@@ -649,6 +647,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                  logfile);
                hook_arg1 = "message";
        } else if (use_message) {
+               char *buffer;
                buffer = strstr(use_message_buffer, "\n\n");
                if (!use_editor && (!buffer || buffer[2] == '\0'))
                        die(_("commit has empty message"));
@@ -753,6 +752,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        /* This checks if committer ident is explicitly given */
        strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT));
        if (use_editor && include_status) {
+               int ident_shown = 0;
+               int saved_color_setting;
                char *ai_tmp, *ci_tmp;
                if (whence != FROM_COMMIT)
                        status_printf_ln(s, GIT_COLOR_NORMAL,
@@ -1406,6 +1407,10 @@ static int git_commit_config(const char *k, const char *v, void *cb)
        }
        if (!strcmp(k, "commit.cleanup"))
                return git_config_string(&cleanup_arg, k, v);
+       if (!strcmp(k, "commit.gpgsign")) {
+               sign_commit = git_config_bool(k, v) ? "" : NULL;
+               return 0;
+       }
 
        status = git_gpg_config(k, v, NULL);
        if (status)
@@ -1510,7 +1515,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
        struct stat statbuf;
-       int allow_fast_forward = 1;
        struct commit *current_head = NULL;
        struct commit_extra_header *extra = NULL;
 
@@ -1558,6 +1562,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        } else if (whence == FROM_MERGE) {
                struct strbuf m = STRBUF_INIT;
                FILE *fp;
+               int allow_fast_forward = 1;
 
                if (!reflog_msg)
                        reflog_msg = "commit (merge)";
index 92ebf23f0a9aa0e052ad68d60e17d0783f633a1c..5677c942b6936f3332b1b4ed12541f45c4228dfb 100644 (file)
@@ -21,8 +21,7 @@ static char key_delim = ' ';
 static char term = '\n';
 
 static int use_global_config, use_system_config, use_local_config;
-static const char *given_config_file;
-static const char *given_config_blob;
+static struct git_config_source given_config_source;
 static int actions, types;
 static const char *get_color_slot, *get_colorbool_slot;
 static int end_null;
@@ -55,8 +54,8 @@ static struct option builtin_config_options[] = {
        OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
        OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
        OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
-       OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
-       OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
+       OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
+       OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
        OPT_GROUP(N_("Action")),
        OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
        OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
@@ -224,8 +223,7 @@ static int get_value(const char *key_, const char *regex_)
        }
 
        git_config_with_options(collect_config, &values,
-                               given_config_file, given_config_blob,
-                               respect_includes);
+                               &given_config_source, respect_includes);
 
        ret = !values.nr;
 
@@ -309,8 +307,7 @@ static void get_color(const char *def_color)
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config_with_options(git_get_color_config, NULL,
-                               given_config_file, given_config_blob,
-                               respect_includes);
+                               &given_config_source, respect_includes);
 
        if (!get_color_found && def_color)
                color_parse(def_color, "command line", parsed_color);
@@ -339,8 +336,7 @@ static int get_colorbool(int print)
        get_diff_color_found = -1;
        get_color_ui_found = -1;
        git_config_with_options(git_get_colorbool_config, NULL,
-                               given_config_file, given_config_blob,
-                               respect_includes);
+                               &given_config_source, respect_includes);
 
        if (get_colorbool_found < 0) {
                if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -362,9 +358,12 @@ static int get_colorbool(int print)
                return get_colorbool_found ? 0 : 1;
 }
 
-static void check_blob_write(void)
+static void check_write(void)
 {
-       if (given_config_blob)
+       if (given_config_source.use_stdin)
+               die("writing to stdin is not supported");
+
+       if (given_config_source.blob)
                die("writing config blobs is not supported");
 }
 
@@ -435,7 +434,7 @@ static int get_urlmatch(const char *var, const char *url)
        }
 
        git_config_with_options(urlmatch_config_entry, &config,
-                               given_config_file, NULL, respect_includes);
+                               &given_config_source, respect_includes);
 
        for_each_string_list_item(item, &values) {
                struct urlmatch_current_candidate_value *matched = item->util;
@@ -464,18 +463,24 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        int nongit = !startup_info->have_repository;
        char *value;
 
-       given_config_file = getenv(CONFIG_ENVIRONMENT);
+       given_config_source.file = getenv(CONFIG_ENVIRONMENT);
 
        argc = parse_options(argc, argv, prefix, builtin_config_options,
                             builtin_config_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
 
        if (use_global_config + use_system_config + use_local_config +
-           !!given_config_file + !!given_config_blob > 1) {
+           !!given_config_source.file + !!given_config_source.blob > 1) {
                error("only one config file at a time.");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
 
+       if (given_config_source.file &&
+                       !strcmp(given_config_source.file, "-")) {
+               given_config_source.file = NULL;
+               given_config_source.use_stdin = 1;
+       }
+
        if (use_global_config) {
                char *user_config = NULL;
                char *xdg_config = NULL;
@@ -493,24 +498,24 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 
                if (access_or_warn(user_config, R_OK, 0) &&
                    xdg_config && !access_or_warn(xdg_config, R_OK, 0))
-                       given_config_file = xdg_config;
+                       given_config_source.file = xdg_config;
                else
-                       given_config_file = user_config;
+                       given_config_source.file = user_config;
        }
        else if (use_system_config)
-               given_config_file = git_etc_gitconfig();
+               given_config_source.file = git_etc_gitconfig();
        else if (use_local_config)
-               given_config_file = git_pathdup("config");
-       else if (given_config_file) {
-               if (!is_absolute_path(given_config_file) && prefix)
-                       given_config_file =
+               given_config_source.file = git_pathdup("config");
+       else if (given_config_source.file) {
+               if (!is_absolute_path(given_config_source.file) && prefix)
+                       given_config_source.file =
                                xstrdup(prefix_filename(prefix,
                                                        strlen(prefix),
-                                                       given_config_file));
+                                                       given_config_source.file));
        }
 
        if (respect_includes == -1)
-               respect_includes = !given_config_file;
+               respect_includes = !given_config_source.file;
 
        if (end_null) {
                term = '\0';
@@ -549,57 +554,58 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        if (actions == ACTION_LIST) {
                check_argc(argc, 0, 0);
                if (git_config_with_options(show_all_config, NULL,
-                                           given_config_file,
-                                           given_config_blob,
+                                           &given_config_source,
                                            respect_includes) < 0) {
-                       if (given_config_file)
+                       if (given_config_source.file)
                                die_errno("unable to read config file '%s'",
-                                         given_config_file);
+                                         given_config_source.file);
                        else
                                die("error processing config file(s)");
                }
        }
        else if (actions == ACTION_EDIT) {
                check_argc(argc, 0, 0);
-               if (!given_config_file && nongit)
+               if (!given_config_source.file && nongit)
                        die("not in a git directory");
-               if (given_config_blob)
+               if (given_config_source.use_stdin)
+                       die("editing stdin is not supported");
+               if (given_config_source.blob)
                        die("editing blobs is not supported");
                git_config(git_default_config, NULL);
-               launch_editor(given_config_file ?
-                             given_config_file : git_path("config"),
+               launch_editor(given_config_source.file ?
+                             given_config_source.file : git_path("config"),
                              NULL, NULL);
        }
        else if (actions == ACTION_SET) {
                int ret;
-               check_blob_write();
+               check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
-               ret = git_config_set_in_file(given_config_file, argv[0], value);
+               ret = git_config_set_in_file(given_config_source.file, argv[0], value);
                if (ret == CONFIG_NOTHING_SET)
                        error("cannot overwrite multiple values with a single value\n"
                        "       Use a regexp, --add or --replace-all to change %s.", argv[0]);
                return ret;
        }
        else if (actions == ACTION_SET_ALL) {
-               check_blob_write();
+               check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
-               return git_config_set_multivar_in_file(given_config_file,
+               return git_config_set_multivar_in_file(given_config_source.file,
                                                       argv[0], value, argv[2], 0);
        }
        else if (actions == ACTION_ADD) {
-               check_blob_write();
+               check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
-               return git_config_set_multivar_in_file(given_config_file,
+               return git_config_set_multivar_in_file(given_config_source.file,
                                                       argv[0], value, "^$", 0);
        }
        else if (actions == ACTION_REPLACE_ALL) {
-               check_blob_write();
+               check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
-               return git_config_set_multivar_in_file(given_config_file,
+               return git_config_set_multivar_in_file(given_config_source.file,
                                                       argv[0], value, argv[2], 1);
        }
        else if (actions == ACTION_GET) {
@@ -623,26 +629,26 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                return get_urlmatch(argv[0], argv[1]);
        }
        else if (actions == ACTION_UNSET) {
-               check_blob_write();
+               check_write();
                check_argc(argc, 1, 2);
                if (argc == 2)
-                       return git_config_set_multivar_in_file(given_config_file,
+                       return git_config_set_multivar_in_file(given_config_source.file,
                                                               argv[0], NULL, argv[1], 0);
                else
-                       return git_config_set_in_file(given_config_file,
+                       return git_config_set_in_file(given_config_source.file,
                                                      argv[0], NULL);
        }
        else if (actions == ACTION_UNSET_ALL) {
-               check_blob_write();
+               check_write();
                check_argc(argc, 1, 2);
-               return git_config_set_multivar_in_file(given_config_file,
+               return git_config_set_multivar_in_file(given_config_source.file,
                                                       argv[0], NULL, argv[1], 1);
        }
        else if (actions == ACTION_RENAME_SECTION) {
                int ret;
-               check_blob_write();
+               check_write();
                check_argc(argc, 2, 2);
-               ret = git_config_rename_section_in_file(given_config_file,
+               ret = git_config_rename_section_in_file(given_config_source.file,
                                                        argv[0], argv[1]);
                if (ret < 0)
                        return ret;
@@ -651,9 +657,9 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        }
        else if (actions == ACTION_REMOVE_SECTION) {
                int ret;
-               check_blob_write();
+               check_write();
                check_argc(argc, 1, 1);
-               ret = git_config_rename_section_in_file(given_config_file,
+               ret = git_config_rename_section_in_file(given_config_source.file,
                                                        argv[0], NULL);
                if (ret < 0)
                        return ret;
index 7db43dae1be95e86c21249d9150fc8553c367527..dadd999c41f6aebe488d057e94e239dfb7dffaec 100644 (file)
@@ -6,7 +6,7 @@
 #include "exec_cmd.h"
 #include "parse-options.h"
 #include "diff.h"
-#include "hash.h"
+#include "hashmap.h"
 #include "argv-array.h"
 
 #define SEEN           (1u << 0)
@@ -25,7 +25,7 @@ static int longformat;
 static int first_parent;
 static int abbrev = -1; /* unspecified */
 static int max_candidates = 10;
-static struct hash_table names;
+static struct hashmap names;
 static int have_util;
 static const char *pattern;
 static int always;
@@ -37,7 +37,7 @@ static const char *diff_index_args[] = {
 };
 
 struct commit_name {
-       struct commit_name *next;
+       struct hashmap_entry entry;
        unsigned char peeled[20];
        struct tag *tag;
        unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
@@ -50,6 +50,12 @@ static const char *prio_names[] = {
        "head", "lightweight", "annotated",
 };
 
+static int commit_name_cmp(const struct commit_name *cn1,
+               const struct commit_name *cn2, const void *peeled)
+{
+       return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled);
+}
+
 static inline unsigned int hash_sha1(const unsigned char *sha1)
 {
        unsigned int hash;
@@ -59,21 +65,9 @@ static inline unsigned int hash_sha1(const unsigned char *sha1)
 
 static inline struct commit_name *find_commit_name(const unsigned char *peeled)
 {
-       struct commit_name *n = lookup_hash(hash_sha1(peeled), &names);
-       while (n && !!hashcmp(peeled, n->peeled))
-               n = n->next;
-       return n;
-}
-
-static int set_util(void *chain, void *data)
-{
-       struct commit_name *n;
-       for (n = chain; n; n = n->next) {
-               struct commit *c = lookup_commit_reference_gently(n->peeled, 1);
-               if (c)
-                       c->util = n;
-       }
-       return 0;
+       struct commit_name key;
+       hashmap_entry_init(&key, hash_sha1(peeled));
+       return hashmap_get(&names, &key, peeled);
 }
 
 static int replace_name(struct commit_name *e,
@@ -118,16 +112,10 @@ static void add_to_known_names(const char *path,
        struct tag *tag = NULL;
        if (replace_name(e, prio, sha1, &tag)) {
                if (!e) {
-                       void **pos;
                        e = xmalloc(sizeof(struct commit_name));
                        hashcpy(e->peeled, peeled);
-                       pos = insert_hash(hash_sha1(peeled), e, &names);
-                       if (pos) {
-                               e->next = *pos;
-                               *pos = e;
-                       } else {
-                               e->next = NULL;
-                       }
+                       hashmap_entry_init(e, hash_sha1(peeled));
+                       hashmap_add(&names, e);
                        e->path = NULL;
                }
                e->tag = tag;
@@ -292,7 +280,14 @@ static void describe(const char *arg, int last_one)
                fprintf(stderr, _("searching to describe %s\n"), arg);
 
        if (!have_util) {
-               for_each_hash(&names, set_util, NULL);
+               struct hashmap_iter iter;
+               struct commit *c;
+               struct commit_name *n = hashmap_iter_first(&names, &iter);
+               for (; n; n = hashmap_iter_next(&iter)) {
+                       c = lookup_commit_reference_gently(n->peeled, 1);
+                       if (c)
+                               c->util = n;
+               }
                have_util = 1;
        }
 
@@ -463,9 +458,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                return cmd_name_rev(args.argc, args.argv, prefix);
        }
 
-       init_hash(&names);
+       hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, 0);
        for_each_rawref(get_name, NULL);
-       if (!names.nr && !always)
+       if (!names.size && !always)
                die(_("No names found, cannot describe anything."));
 
        if (argc == 0) {
index 025bc3e38d7d8055699ea15f241648602cfaf267..55f457c04f5c72ca31ab4fbc28e62ea48b6cb471 100644 (file)
@@ -1026,7 +1026,6 @@ static int fetch_multiple(struct string_list *list)
 
 static int fetch_one(struct remote *remote, int argc, const char **argv)
 {
-       int i;
        static const char **refs = NULL;
        struct refspec *refspec;
        int ref_nr = 0;
@@ -1050,6 +1049,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 
        if (argc > 0) {
                int j = 0;
+               int i;
                refs = xcalloc(argc + 1, sizeof(const char *));
                for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
index c19545d49e217400ee1736183c870557b9c9d54b..63d400bcb2092b1751427994b35057f72b04de17 100644 (file)
@@ -29,6 +29,7 @@ static int pack_refs = 1;
 static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
+static int detach_auto = 1;
 static const char *prune_expire = "2.weeks.ago";
 
 static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
@@ -73,6 +74,10 @@ static int gc_config(const char *var, const char *value, void *cb)
                gc_auto_pack_limit = git_config_int(var, value);
                return 0;
        }
+       if (!strcmp(var, "gc.autodetach")) {
+               detach_auto = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "gc.pruneexpire")) {
                if (value && strcmp(value, "now")) {
                        unsigned long now = approxidate("now");
@@ -188,13 +193,12 @@ static int need_to_gc(void)
 static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 {
        static struct lock_file lock;
-       static char locking_host[128];
        char my_host[128];
        struct strbuf sb = STRBUF_INIT;
        struct stat st;
        uintmax_t pid;
        FILE *fp;
-       int fd, should_exit;
+       int fd;
 
        if (pidfile)
                /* already locked */
@@ -206,6 +210,8 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
        fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
                                       LOCK_DIE_ON_ERROR);
        if (!force) {
+               static char locking_host[128];
+               int should_exit;
                fp = fopen(git_path("gc.pid"), "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
@@ -301,11 +307,19 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                 */
                if (!need_to_gc())
                        return 0;
-               if (!quiet)
-                       fprintf(stderr,
-                                       _("Auto packing the repository for optimum performance. You may also\n"
-                                       "run \"git gc\" manually. See "
-                                       "\"git help gc\" for more information.\n"));
+               if (!quiet) {
+                       if (detach_auto)
+                               fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
+                       else
+                               fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
+                       fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
+               }
+               if (detach_auto)
+                       /*
+                        * failure to daemonize is ok, we'll continue
+                        * in foreground
+                        */
+                       daemonize();
        } else
                add_repack_all_option();
 
index 63f86032d91f00fc607f7d3b26ec941bb7a4c76c..69ac2d8797ec32dc206425db0a2f3d7356810cfe 100644 (file)
@@ -379,7 +379,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
                const struct cache_entry *ce = active_cache[nr];
                if (!S_ISREG(ce->ce_mode))
                        continue;
-               if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+               if (!ce_path_match(ce, pathspec, NULL))
                        continue;
                /*
                 * If CE_VALID is on, we assume worktree file and its cache entry
@@ -524,9 +524,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
 
        fill_directory(&dir, pathspec);
        for (i = 0; i < dir.nr; i++) {
-               const char *name = dir.entries[i]->name;
-               int namelen = strlen(name);
-               if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL))
+               if (!dir_path_match(dir.entries[i], pathspec, 0, NULL))
                        continue;
                hit |= grep_file(opt, dir.entries[i]->name);
                if (hit && opt->status_only)
index e1cf6d8547d4aa7f548fb80f0efb8f4e7b1d9c8e..47c38808a26a4602f8bd2c7d87f67770a625c679 100644 (file)
@@ -64,7 +64,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
        if (len >= ent->len)
                die("git ls-files: internal error - directory entry not superset of prefix");
 
-       if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched))
+       if (!dir_path_match(ent, &pathspec, len, ps_matched))
                return;
 
        fputs(tag, stdout);
@@ -139,7 +139,9 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)
        if (len >= ce_namelen(ce))
                die("git ls-files: internal error - cache entry not superset of prefix");
 
-       if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched))
+       if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),
+                           len, ps_matched,
+                           S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))
                return;
 
        if (tag && *tag && show_valid_bit &&
@@ -195,7 +197,8 @@ static void show_ru_info(void)
                len = strlen(path);
                if (len < max_prefix_len)
                        continue; /* outside of the prefix */
-               if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched))
+               if (!match_pathspec(&pathspec, path, len,
+                                   max_prefix_len, ps_matched, 0))
                        continue; /* uninterested */
                for (i = 0; i < 3; i++) {
                        if (!ui->mode[i])
index 65ec93184614619cac925c17ef3ebc90cd30a82c..51184dfa2efa46323e79c4504121bf5fe948a6d2 100644 (file)
@@ -171,7 +171,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
         * show_recursive() rolls its own matching code and is
         * generally ignorant of 'struct pathspec'. The magic mask
         * cannot be lifted until it is converted to use
-        * match_pathspec_depth() or tree_entry_interesting()
+        * match_pathspec() or tree_entry_interesting()
         */
        parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE,
                       PATHSPEC_PREFER_CWD,
index e576a7fdc619303a65e859920a1ab6936db2626c..f0cf1205faa66c5ae8b4603f04239f1efbd999d6 100644 (file)
@@ -597,6 +597,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
        } else if (!strcmp(k, "merge.defaulttoupstream")) {
                default_to_upstream = git_config_bool(k, v);
                return 0;
+       } else if (!strcmp(k, "commit.gpgsign")) {
+               sign_commit = git_config_bool(k, v) ? "" : NULL;
+               return 0;
        }
 
        status = fmt_merge_msg_config(k, v, cb);
index 21c46d1636e6e87dd9eaaca60ac08ebbbc4efec3..7e26eb5229a0c8a526314cecb9860c3dfb0fed00 100644 (file)
@@ -162,7 +162,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                        if (strncmp(path, src_w_slash, len_w_slash))
                                                break;
                                }
-                               free((char *)src_w_slash);
+                               if (src_w_slash != src)
+                                       free((char *)src_w_slash);
 
                                if (last - first < 1)
                                        bad = _("source directory is empty");
index 541667f1026d7ba62be8e029c31138d5661f65c7..c73337931b0c2d456801de75a4a17e83b2091753 100644 (file)
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
+#include "pack-objects.h"
 #include "progress.h"
 #include "refs.h"
 #include "streaming.h"
 #include "thread-utils.h"
+#include "pack-bitmap.h"
 
 static const char *pack_usage[] = {
        N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@ -25,42 +27,15 @@ static const char *pack_usage[] = {
        NULL
 };
 
-struct object_entry {
-       struct pack_idx_entry idx;
-       unsigned long size;     /* uncompressed size */
-       struct packed_git *in_pack;     /* already in pack */
-       off_t in_pack_offset;
-       struct object_entry *delta;     /* delta base object */
-       struct object_entry *delta_child; /* deltified objects who bases me */
-       struct object_entry *delta_sibling; /* other deltified objects who
-                                            * uses the same base as me
-                                            */
-       void *delta_data;       /* cached delta (uncompressed) */
-       unsigned long delta_size;       /* delta data size (uncompressed) */
-       unsigned long z_delta_size;     /* delta data size (compressed) */
-       enum object_type type;
-       enum object_type in_pack_type;  /* could be delta */
-       uint32_t hash;                  /* name hint hash */
-       unsigned char in_pack_header_size;
-       unsigned preferred_base:1; /*
-                                   * we do not pack this, but is available
-                                   * to be used as the base object to delta
-                                   * objects against.
-                                   */
-       unsigned no_try_delta:1;
-       unsigned tagged:1; /* near the very tip of refs */
-       unsigned filled:1; /* assigned write-order */
-};
-
 /*
- * Objects we are going to pack are collected in objects array (dynamically
- * expanded).  nr_objects & nr_alloc controls this array.  They are stored
- * in the order we see -- typically rev-list --objects order that gives us
- * nice "minimum seek" order.
+ * Objects we are going to pack are collected in the `to_pack` structure.
+ * It contains an array (dynamically expanded) of the object data, and a map
+ * that can resolve SHA1s to their position in the array.
  */
-static struct object_entry *objects;
+static struct packing_data to_pack;
+
 static struct pack_idx_entry **written_list;
-static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
+static uint32_t nr_result, nr_written;
 
 static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
@@ -83,27 +58,43 @@ static struct progress *progress_state;
 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
 static int pack_compression_seen;
 
+static struct packed_git *reuse_packfile;
+static uint32_t reuse_packfile_objects;
+static off_t reuse_packfile_offset;
+
+static int use_bitmap_index = 1;
+static int write_bitmap_index;
+static uint16_t write_bitmap_options;
+
 static unsigned long delta_cache_size = 0;
 static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
 static unsigned long cache_max_small_delta_size = 1000;
 
 static unsigned long window_memory_limit = 0;
 
-/*
- * The object names in objects array are hashed with this hashtable,
- * to help looking up the entry by object name.
- * This hashtable is built after all the objects are seen.
- */
-static int *object_ix;
-static int object_ix_hashsz;
-static struct object_entry *locate_object_entry(const unsigned char *sha1);
-
 /*
  * stats
  */
 static uint32_t written, written_delta;
 static uint32_t reused, reused_delta;
 
+/*
+ * Indexed commits
+ */
+static struct commit **indexed_commits;
+static unsigned int indexed_commits_nr;
+static unsigned int indexed_commits_alloc;
+
+static void index_commit_for_bitmap(struct commit *commit)
+{
+       if (indexed_commits_nr >= indexed_commits_alloc) {
+               indexed_commits_alloc = (indexed_commits_alloc + 32) * 2;
+               indexed_commits = xrealloc(indexed_commits,
+                       indexed_commits_alloc * sizeof(struct commit *));
+       }
+
+       indexed_commits[indexed_commits_nr++] = commit;
+}
 
 static void *get_delta(struct object_entry *entry)
 {
@@ -553,12 +544,12 @@ static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
                       void *cb_data)
 {
        unsigned char peeled[20];
-       struct object_entry *entry = locate_object_entry(sha1);
+       struct object_entry *entry = packlist_find(&to_pack, sha1, NULL);
 
        if (entry)
                entry->tagged = 1;
        if (!peel_ref(path, peeled)) {
-               entry = locate_object_entry(peeled);
+               entry = packlist_find(&to_pack, peeled, NULL);
                if (entry)
                        entry->tagged = 1;
        }
@@ -633,9 +624,10 @@ static struct object_entry **compute_write_order(void)
 {
        unsigned int i, wo_end, last_untagged;
 
-       struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));
+       struct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo));
+       struct object_entry *objects = to_pack.objects;
 
-       for (i = 0; i < nr_objects; i++) {
+       for (i = 0; i < to_pack.nr_objects; i++) {
                objects[i].tagged = 0;
                objects[i].filled = 0;
                objects[i].delta_child = NULL;
@@ -647,7 +639,7 @@ static struct object_entry **compute_write_order(void)
         * Make sure delta_sibling is sorted in the original
         * recency order.
         */
-       for (i = nr_objects; i > 0;) {
+       for (i = to_pack.nr_objects; i > 0;) {
                struct object_entry *e = &objects[--i];
                if (!e->delta)
                        continue;
@@ -665,7 +657,7 @@ static struct object_entry **compute_write_order(void)
         * Give the objects in the original recency order until
         * we see a tagged tip.
         */
-       for (i = wo_end = 0; i < nr_objects; i++) {
+       for (i = wo_end = 0; i < to_pack.nr_objects; i++) {
                if (objects[i].tagged)
                        break;
                add_to_write_order(wo, &wo_end, &objects[i]);
@@ -675,7 +667,7 @@ static struct object_entry **compute_write_order(void)
        /*
         * Then fill all the tagged tips.
         */
-       for (; i < nr_objects; i++) {
+       for (; i < to_pack.nr_objects; i++) {
                if (objects[i].tagged)
                        add_to_write_order(wo, &wo_end, &objects[i]);
        }
@@ -683,7 +675,7 @@ static struct object_entry **compute_write_order(void)
        /*
         * And then all remaining commits and tags.
         */
-       for (i = last_untagged; i < nr_objects; i++) {
+       for (i = last_untagged; i < to_pack.nr_objects; i++) {
                if (objects[i].type != OBJ_COMMIT &&
                    objects[i].type != OBJ_TAG)
                        continue;
@@ -693,7 +685,7 @@ static struct object_entry **compute_write_order(void)
        /*
         * And then all the trees.
         */
-       for (i = last_untagged; i < nr_objects; i++) {
+       for (i = last_untagged; i < to_pack.nr_objects; i++) {
                if (objects[i].type != OBJ_TREE)
                        continue;
                add_to_write_order(wo, &wo_end, &objects[i]);
@@ -702,17 +694,57 @@ static struct object_entry **compute_write_order(void)
        /*
         * Finally all the rest in really tight order
         */
-       for (i = last_untagged; i < nr_objects; i++) {
+       for (i = last_untagged; i < to_pack.nr_objects; i++) {
                if (!objects[i].filled)
                        add_family_to_write_order(wo, &wo_end, &objects[i]);
        }
 
-       if (wo_end != nr_objects)
-               die("ordered %u objects, expected %"PRIu32, wo_end, nr_objects);
+       if (wo_end != to_pack.nr_objects)
+               die("ordered %u objects, expected %"PRIu32, wo_end, to_pack.nr_objects);
 
        return wo;
 }
 
+static off_t write_reused_pack(struct sha1file *f)
+{
+       unsigned char buffer[8192];
+       off_t to_write;
+       int fd;
+
+       if (!is_pack_valid(reuse_packfile))
+               die("packfile is invalid: %s", reuse_packfile->pack_name);
+
+       fd = git_open_noatime(reuse_packfile->pack_name);
+       if (fd < 0)
+               die_errno("unable to open packfile for reuse: %s",
+                         reuse_packfile->pack_name);
+
+       if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1)
+               die_errno("unable to seek in reused packfile");
+
+       if (reuse_packfile_offset < 0)
+               reuse_packfile_offset = reuse_packfile->pack_size - 20;
+
+       to_write = reuse_packfile_offset - sizeof(struct pack_header);
+
+       while (to_write) {
+               int read_pack = xread(fd, buffer, sizeof(buffer));
+
+               if (read_pack <= 0)
+                       die_errno("unable to read from reused packfile");
+
+               if (read_pack > to_write)
+                       read_pack = to_write;
+
+               sha1write(f, buffer, read_pack);
+               to_write -= read_pack;
+       }
+
+       close(fd);
+       written += reuse_packfile_objects;
+       return reuse_packfile_offset - sizeof(struct pack_header);
+}
+
 static void write_pack_file(void)
 {
        uint32_t i = 0, j;
@@ -724,7 +756,7 @@ static void write_pack_file(void)
 
        if (progress > pack_to_stdout)
                progress_state = start_progress("Writing objects", nr_result);
-       written_list = xmalloc(nr_objects * sizeof(*written_list));
+       written_list = xmalloc(to_pack.nr_objects * sizeof(*written_list));
        write_order = compute_write_order();
 
        do {
@@ -737,8 +769,17 @@ static void write_pack_file(void)
                        f = create_tmp_packfile(&pack_tmp_name);
 
                offset = write_pack_header(f, nr_remaining);
+
+               if (reuse_packfile) {
+                       off_t packfile_size;
+                       assert(pack_to_stdout);
+
+                       packfile_size = write_reused_pack(f);
+                       offset += packfile_size;
+               }
+
                nr_written = 0;
-               for (; i < nr_objects; i++) {
+               for (; i < to_pack.nr_objects; i++) {
                        struct object_entry *e = write_order[i];
                        if (write_one(f, e, &offset) == WRITE_ONE_BREAK)
                                break;
@@ -789,9 +830,31 @@ static void write_pack_file(void)
                        if (sizeof(tmpname) <= strlen(base_name) + 50)
                                die("pack base name '%s' too long", base_name);
                        snprintf(tmpname, sizeof(tmpname), "%s-", base_name);
+
+                       if (write_bitmap_index) {
+                               bitmap_writer_set_checksum(sha1);
+                               bitmap_writer_build_type_index(written_list, nr_written);
+                       }
+
                        finish_tmp_packfile(tmpname, pack_tmp_name,
                                            written_list, nr_written,
                                            &pack_idx_opts, sha1);
+
+                       if (write_bitmap_index) {
+                               char *end_of_name_prefix = strrchr(tmpname, 0);
+                               sprintf(end_of_name_prefix, "%s.bitmap", sha1_to_hex(sha1));
+
+                               stop_progress(&progress_state);
+
+                               bitmap_writer_show_progress(progress);
+                               bitmap_writer_reuse_bitmaps(&to_pack);
+                               bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
+                               bitmap_writer_build(&to_pack);
+                               bitmap_writer_finish(written_list, nr_written,
+                                                    tmpname, write_bitmap_options);
+                               write_bitmap_index = 0;
+                       }
+
                        free(pack_tmp_name);
                        puts(sha1_to_hex(sha1));
                }
@@ -801,7 +864,7 @@ static void write_pack_file(void)
                        written_list[j]->offset = (off_t)-1;
                }
                nr_remaining -= nr_written;
-       } while (nr_remaining && i < nr_objects);
+       } while (nr_remaining && i < to_pack.nr_objects);
 
        free(written_list);
        free(write_order);
@@ -811,73 +874,6 @@ static void write_pack_file(void)
                        written, nr_result);
 }
 
-static int locate_object_entry_hash(const unsigned char *sha1)
-{
-       int i;
-       unsigned int ui;
-       memcpy(&ui, sha1, sizeof(unsigned int));
-       i = ui % object_ix_hashsz;
-       while (0 < object_ix[i]) {
-               if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))
-                       return i;
-               if (++i == object_ix_hashsz)
-                       i = 0;
-       }
-       return -1 - i;
-}
-
-static struct object_entry *locate_object_entry(const unsigned char *sha1)
-{
-       int i;
-
-       if (!object_ix_hashsz)
-               return NULL;
-
-       i = locate_object_entry_hash(sha1);
-       if (0 <= i)
-               return &objects[object_ix[i]-1];
-       return NULL;
-}
-
-static void rehash_objects(void)
-{
-       uint32_t i;
-       struct object_entry *oe;
-
-       object_ix_hashsz = nr_objects * 3;
-       if (object_ix_hashsz < 1024)
-               object_ix_hashsz = 1024;
-       object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
-       memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
-       for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
-               int ix = locate_object_entry_hash(oe->idx.sha1);
-               if (0 <= ix)
-                       continue;
-               ix = -1 - ix;
-               object_ix[ix] = i + 1;
-       }
-}
-
-static uint32_t name_hash(const char *name)
-{
-       uint32_t c, hash = 0;
-
-       if (!name)
-               return 0;
-
-       /*
-        * This effectively just creates a sortable number from the
-        * last sixteen non-whitespace characters. Last characters
-        * count "most", so things that end in ".c" sort together.
-        */
-       while ((c = *name++) != 0) {
-               if (isspace(c))
-                       continue;
-               hash = (hash >> 2) + (c << 24);
-       }
-       return hash;
-}
-
 static void setup_delta_attr_check(struct git_attr_check *check)
 {
        static struct git_attr *attr_delta;
@@ -900,42 +896,69 @@ static int no_try_delta(const char *path)
        return 0;
 }
 
-static int add_object_entry(const unsigned char *sha1, enum object_type type,
-                           const char *name, int exclude)
+/*
+ * When adding an object, check whether we have already added it
+ * to our packing list. If so, we can skip. However, if we are
+ * being asked to excludei t, but the previous mention was to include
+ * it, make sure to adjust its flags and tweak our numbers accordingly.
+ *
+ * As an optimization, we pass out the index position where we would have
+ * found the item, since that saves us from having to look it up again a
+ * few lines later when we want to add the new entry.
+ */
+static int have_duplicate_entry(const unsigned char *sha1,
+                               int exclude,
+                               uint32_t *index_pos)
 {
        struct object_entry *entry;
-       struct packed_git *p, *found_pack = NULL;
-       off_t found_offset = 0;
-       int ix;
-       uint32_t hash = name_hash(name);
-
-       ix = nr_objects ? locate_object_entry_hash(sha1) : -1;
-       if (ix >= 0) {
-               if (exclude) {
-                       entry = objects + object_ix[ix] - 1;
-                       if (!entry->preferred_base)
-                               nr_result--;
-                       entry->preferred_base = 1;
-               }
+
+       entry = packlist_find(&to_pack, sha1, index_pos);
+       if (!entry)
                return 0;
+
+       if (exclude) {
+               if (!entry->preferred_base)
+                       nr_result--;
+               entry->preferred_base = 1;
        }
 
+       return 1;
+}
+
+/*
+ * Check whether we want the object in the pack (e.g., we do not want
+ * objects found in non-local stores if the "--local" option was used).
+ *
+ * As a side effect of this check, we will find the packed version of this
+ * object, if any. We therefore pass out the pack information to avoid having
+ * to look it up again later.
+ */
+static int want_object_in_pack(const unsigned char *sha1,
+                              int exclude,
+                              struct packed_git **found_pack,
+                              off_t *found_offset)
+{
+       struct packed_git *p;
+
        if (!exclude && local && has_loose_object_nonlocal(sha1))
                return 0;
 
+       *found_pack = NULL;
+       *found_offset = 0;
+
        for (p = packed_git; p; p = p->next) {
                off_t offset = find_pack_entry_one(sha1, p);
                if (offset) {
-                       if (!found_pack) {
+                       if (!*found_pack) {
                                if (!is_pack_valid(p)) {
                                        warning("packfile %s cannot be accessed", p->pack_name);
                                        continue;
                                }
-                               found_offset = offset;
-                               found_pack = p;
+                               *found_offset = offset;
+                               *found_pack = p;
                        }
                        if (exclude)
-                               break;
+                               return 1;
                        if (incremental)
                                return 0;
                        if (local && !p->pack_local)
@@ -945,14 +968,21 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
                }
        }
 
-       if (nr_objects >= nr_alloc) {
-               nr_alloc = (nr_alloc  + 1024) * 3 / 2;
-               objects = xrealloc(objects, nr_alloc * sizeof(*entry));
-       }
+       return 1;
+}
+
+static void create_object_entry(const unsigned char *sha1,
+                               enum object_type type,
+                               uint32_t hash,
+                               int exclude,
+                               int no_try_delta,
+                               uint32_t index_pos,
+                               struct packed_git *found_pack,
+                               off_t found_offset)
+{
+       struct object_entry *entry;
 
-       entry = objects + nr_objects++;
-       memset(entry, 0, sizeof(*entry));
-       hashcpy(entry->idx.sha1, sha1);
+       entry = packlist_alloc(&to_pack, sha1, index_pos);
        entry->hash = hash;
        if (type)
                entry->type = type;
@@ -965,16 +995,43 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
                entry->in_pack_offset = found_offset;
        }
 
-       if (object_ix_hashsz * 3 <= nr_objects * 4)
-               rehash_objects();
-       else
-               object_ix[-1 - ix] = nr_objects;
+       entry->no_try_delta = no_try_delta;
+}
+
+static int add_object_entry(const unsigned char *sha1, enum object_type type,
+                           const char *name, int exclude)
+{
+       struct packed_git *found_pack;
+       off_t found_offset;
+       uint32_t index_pos;
 
-       display_progress(progress_state, nr_objects);
+       if (have_duplicate_entry(sha1, exclude, &index_pos))
+               return 0;
+
+       if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset))
+               return 0;
 
-       if (name && no_try_delta(name))
-               entry->no_try_delta = 1;
+       create_object_entry(sha1, type, pack_name_hash(name),
+                           exclude, name && no_try_delta(name),
+                           index_pos, found_pack, found_offset);
 
+       display_progress(progress_state, to_pack.nr_objects);
+       return 1;
+}
+
+static int add_object_entry_from_bitmap(const unsigned char *sha1,
+                                       enum object_type type,
+                                       int flags, uint32_t name_hash,
+                                       struct packed_git *pack, off_t offset)
+{
+       uint32_t index_pos;
+
+       if (have_duplicate_entry(sha1, 0, &index_pos))
+               return 0;
+
+       create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
+
+       display_progress(progress_state, to_pack.nr_objects);
        return 1;
 }
 
@@ -1175,7 +1232,7 @@ static void add_preferred_base_object(const char *name)
 {
        struct pbase_tree *it;
        int cmplen;
-       unsigned hash = name_hash(name);
+       unsigned hash = pack_name_hash(name);
 
        if (!num_preferred_base || check_pbase_path(hash))
                return;
@@ -1327,7 +1384,7 @@ static void check_object(struct object_entry *entry)
                        break;
                }
 
-               if (base_ref && (base_entry = locate_object_entry(base_ref))) {
+               if (base_ref && (base_entry = packlist_find(&to_pack, base_ref, NULL))) {
                        /*
                         * If base_ref was set above that means we wish to
                         * reuse delta data, and we even found that base
@@ -1401,12 +1458,12 @@ static void get_object_details(void)
        uint32_t i;
        struct object_entry **sorted_by_offset;
 
-       sorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *));
-       for (i = 0; i < nr_objects; i++)
-               sorted_by_offset[i] = objects + i;
-       qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
+       sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *));
+       for (i = 0; i < to_pack.nr_objects; i++)
+               sorted_by_offset[i] = to_pack.objects + i;
+       qsort(sorted_by_offset, to_pack.nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
 
-       for (i = 0; i < nr_objects; i++) {
+       for (i = 0; i < to_pack.nr_objects; i++) {
                struct object_entry *entry = sorted_by_offset[i];
                check_object(entry);
                if (big_file_threshold < entry->size)
@@ -2032,7 +2089,7 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo
 
        if (starts_with(path, "refs/tags/") && /* is a tag? */
            !peel_ref(path, peeled)        && /* peelable? */
-           locate_object_entry(peeled))      /* object packed? */
+           packlist_find(&to_pack, peeled, NULL))      /* object packed? */
                add_object_entry(sha1, OBJ_TAG, NULL, 0);
        return 0;
 }
@@ -2055,14 +2112,14 @@ static void prepare_pack(int window, int depth)
        if (!pack_to_stdout)
                do_check_packed_object_crc = 1;
 
-       if (!nr_objects || !window || !depth)
+       if (!to_pack.nr_objects || !window || !depth)
                return;
 
-       delta_list = xmalloc(nr_objects * sizeof(*delta_list));
+       delta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list));
        nr_deltas = n = 0;
 
-       for (i = 0; i < nr_objects; i++) {
-               struct object_entry *entry = objects + i;
+       for (i = 0; i < to_pack.nr_objects; i++) {
+               struct object_entry *entry = to_pack.objects + i;
 
                if (entry->delta)
                        /* This happens if we decided to reuse existing
@@ -2140,6 +2197,20 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                cache_max_small_delta_size = git_config_int(k, v);
                return 0;
        }
+       if (!strcmp(k, "pack.writebitmaps")) {
+               write_bitmap_index = git_config_bool(k, v);
+               return 0;
+       }
+       if (!strcmp(k, "pack.writebitmaphashcache")) {
+               if (git_config_bool(k, v))
+                       write_bitmap_options |= BITMAP_OPT_HASH_CACHE;
+               else
+                       write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE;
+       }
+       if (!strcmp(k, "pack.usebitmaps")) {
+               use_bitmap_index = git_config_bool(k, v);
+               return 0;
+       }
        if (!strcmp(k, "pack.threads")) {
                delta_search_threads = git_config_int(k, v);
                if (delta_search_threads < 0)
@@ -2198,6 +2269,9 @@ static void show_commit(struct commit *commit, void *data)
 {
        add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
        commit->object.flags |= OBJECT_ADDED;
+
+       if (write_bitmap_index)
+               index_commit_for_bitmap(commit);
 }
 
 static void show_object(struct object *obj,
@@ -2340,7 +2414,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
 
                for (i = 0; i < p->num_objects; i++) {
                        sha1 = nth_packed_object_sha1(p, i);
-                       if (!locate_object_entry(sha1) &&
+                       if (!packlist_find(&to_pack, sha1, NULL) &&
                                !has_sha1_pack_kept_or_nonlocal(sha1))
                                if (force_object_loose(sha1, p->mtime))
                                        die("unable to force loose object");
@@ -2348,6 +2422,29 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
        }
 }
 
+static int get_object_list_from_bitmap(struct rev_info *revs)
+{
+       if (prepare_bitmap_walk(revs) < 0)
+               return -1;
+
+       if (!reuse_partial_packfile_from_bitmap(
+                       &reuse_packfile,
+                       &reuse_packfile_objects,
+                       &reuse_packfile_offset)) {
+               assert(reuse_packfile_objects);
+               nr_result += reuse_packfile_objects;
+
+               if (progress) {
+                       fprintf(stderr, "Reusing existing pack: %d, done.\n",
+                               reuse_packfile_objects);
+                       fflush(stderr);
+               }
+       }
+
+       traverse_bitmap_commit_list(&add_object_entry_from_bitmap);
+       return 0;
+}
+
 static void get_object_list(int ac, const char **av)
 {
        struct rev_info revs;
@@ -2367,6 +2464,7 @@ static void get_object_list(int ac, const char **av)
                if (*line == '-') {
                        if (!strcmp(line, "--not")) {
                                flags ^= UNINTERESTING;
+                               write_bitmap_index = 0;
                                continue;
                        }
                        die("not a rev '%s'", line);
@@ -2375,6 +2473,9 @@ static void get_object_list(int ac, const char **av)
                        die("bad revision '%s'", line);
        }
 
+       if (use_bitmap_index && !get_object_list_from_bitmap(&revs))
+               return;
+
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(&revs, show_edge);
@@ -2504,6 +2605,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                            N_("pack compression level")),
                OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
                            N_("do not hide commits by grafts"), 0),
+               OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index,
+                        N_("use a bitmap index if available to speed up counting objects")),
+               OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index,
+                        N_("write a bitmap index together with the pack index")),
                OPT_END(),
        };
 
@@ -2570,6 +2675,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (keep_unreachable && unpack_unreachable)
                die("--keep-unreachable and --unpack-unreachable are incompatible.");
 
+       if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow())
+               use_bitmap_index = 0;
+
+       if (pack_to_stdout || !rev_list_all)
+               write_bitmap_index = 0;
+
        if (progress && all_progress_implied)
                progress = 2;
 
index 0e50ddbb01342128d9118217118726000bdeaff6..f8dfea41e1ad8b6d888c1a2adc13eee87083491e 100644 (file)
@@ -26,7 +26,6 @@ static struct push_cas_option cas;
 static const char **refspec;
 static int refspec_nr;
 static int refspec_alloc;
-static int default_matching_used;
 
 static void add_refspec(const char *ref)
 {
@@ -59,7 +58,7 @@ static const char *map_refspec(const char *ref,
        }
 
        if (push_default == PUSH_DEFAULT_UPSTREAM &&
-           !prefixcmp(matched->name, "refs/heads/")) {
+           starts_with(matched->name, "refs/heads/")) {
                struct branch *branch = branch_get(matched->name + 11);
                if (branch->merge_nr == 1 && branch->merge[0]->src) {
                        struct strbuf buf = STRBUF_INIT;
@@ -204,9 +203,9 @@ static void setup_push_current(struct remote *remote, struct branch *branch)
 }
 
 static char warn_unspecified_push_default_msg[] =
-N_("push.default is unset; its implicit value is changing in\n"
+N_("push.default is unset; its implicit value has changed in\n"
    "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
-   "and maintain the current behavior after the default changes, use:\n"
+   "and maintain the traditional behavior, use:\n"
    "\n"
    "  git config --global push.default matching\n"
    "\n"
@@ -217,7 +216,7 @@ N_("push.default is unset; its implicit value is changing in\n"
    "When push.default is set to 'matching', git will push local branches\n"
    "to the remote branches that already exist with the same name.\n"
    "\n"
-   "In Git 2.0, Git will default to the more conservative 'simple'\n"
+   "Since Git 2.0, Git defaults to the more conservative 'simple'\n"
    "behavior, which only pushes the current branch to the corresponding\n"
    "remote branch that 'git pull' uses to update the current branch.\n"
    "\n"
@@ -247,14 +246,14 @@ static void setup_default_push_refspecs(struct remote *remote)
 
        switch (push_default) {
        default:
-       case PUSH_DEFAULT_UNSPECIFIED:
-               default_matching_used = 1;
-               warn_unspecified_push_default_configuration();
-               /* fallthru */
        case PUSH_DEFAULT_MATCHING:
                add_refspec(":");
                break;
 
+       case PUSH_DEFAULT_UNSPECIFIED:
+               warn_unspecified_push_default_configuration();
+               /* fallthru */
+
        case PUSH_DEFAULT_SIMPLE:
                if (triangular)
                        setup_push_current(remote, branch);
@@ -283,12 +282,6 @@ static const char message_advice_pull_before_push[] =
           "'git pull ...') before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
-static const char message_advice_use_upstream[] =
-       N_("Updates were rejected because a pushed branch tip is behind its remote\n"
-          "counterpart. If you did not intend to push that branch, you may want to\n"
-          "specify branches to push or set the 'push.default' configuration variable\n"
-          "to 'simple', 'current' or 'upstream' to push only the current branch.");
-
 static const char message_advice_checkout_pull_push[] =
        N_("Updates were rejected because a pushed branch tip is behind its remote\n"
           "counterpart. Check out this branch and integrate the remote changes\n"
@@ -317,13 +310,6 @@ static void advise_pull_before_push(void)
        advise(_(message_advice_pull_before_push));
 }
 
-static void advise_use_upstream(void)
-{
-       if (!advice_push_non_ff_default || !advice_push_update_rejected)
-               return;
-       advise(_(message_advice_use_upstream));
-}
-
 static void advise_checkout_pull_push(void)
 {
        if (!advice_push_non_ff_matching || !advice_push_update_rejected)
@@ -385,10 +371,7 @@ static int push_with_options(struct transport *transport, int flags)
        if (reject_reasons & REJECT_NON_FF_HEAD) {
                advise_pull_before_push();
        } else if (reject_reasons & REJECT_NON_FF_OTHER) {
-               if (default_matching_used)
-                       advise_use_upstream();
-               else
-                       advise_checkout_pull_push();
+               advise_checkout_pull_push();
        } else if (reject_reasons & REJECT_ALREADY_EXISTS) {
                advise_ref_already_exists();
        } else if (reject_reasons & REJECT_FETCH_FIRST) {
index bb2314c9cb0ce3e01372c6c3a27396931c0143ab..49f5857627fd616b1019063f3f2d1937b20004fd 100644 (file)
@@ -94,7 +94,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
 
 static void remove_redundant_pack(const char *dir_name, const char *base_name)
 {
-       const char *exts[] = {".pack", ".idx", ".keep"};
+       const char *exts[] = {".pack", ".idx", ".keep", ".bitmap"};
        int i;
        struct strbuf buf = STRBUF_INIT;
        size_t plen;
@@ -115,7 +115,14 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name)
 
 int cmd_repack(int argc, const char **argv, const char *prefix)
 {
-       const char *exts[2] = {".pack", ".idx"};
+       struct {
+               const char *name;
+               unsigned optional:1;
+       } exts[] = {
+               {".pack"},
+               {".idx"},
+               {".bitmap", 1},
+       };
        struct child_process cmd;
        struct string_list_item *item;
        struct argv_array cmd_args = ARGV_ARRAY_INIT;
@@ -137,6 +144,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        int no_update_server_info = 0;
        int quiet = 0;
        int local = 0;
+       int write_bitmap = -1;
 
        struct option builtin_repack_options[] = {
                OPT_BIT('a', NULL, &pack_everything,
@@ -155,6 +163,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                OPT__QUIET(&quiet, N_("be quiet")),
                OPT_BOOL('l', "local", &local,
                                N_("pass --local to git-pack-objects")),
+               OPT_BOOL('b', "write-bitmap-index", &write_bitmap,
+                               N_("write bitmap index")),
                OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
                                N_("with -A, do not loosen objects older than this")),
                OPT_STRING(0, "window", &window, N_("n"),
@@ -196,6 +206,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                argv_array_pushf(&cmd_args, "--no-reuse-delta");
        if (no_reuse_object)
                argv_array_pushf(&cmd_args, "--no-reuse-object");
+       if (write_bitmap >= 0)
+               argv_array_pushf(&cmd_args, "--%swrite-bitmap-index",
+                                write_bitmap ? "" : "no-");
 
        if (pack_everything & ALL_INTO_ONE) {
                get_non_kept_pack_filenames(&existing_packs);
@@ -256,17 +269,17 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
         */
        failed = 0;
        for_each_string_list_item(item, &names) {
-               for (ext = 0; ext < 2; ext++) {
+               for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
                        char *fname, *fname_old;
                        fname = mkpathdup("%s/pack-%s%s", packdir,
-                                               item->string, exts[ext]);
+                                               item->string, exts[ext].name);
                        if (!file_exists(fname)) {
                                free(fname);
                                continue;
                        }
 
                        fname_old = mkpath("%s/old-%s%s", packdir,
-                                               item->string, exts[ext]);
+                                               item->string, exts[ext].name);
                        if (file_exists(fname_old))
                                if (unlink(fname_old))
                                        failed = 1;
@@ -313,19 +326,23 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        /* Now the ones with the same name are out of the way... */
        for_each_string_list_item(item, &names) {
-               for (ext = 0; ext < 2; ext++) {
+               for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
                        char *fname, *fname_old;
                        struct stat statbuffer;
+                       int exists = 0;
                        fname = mkpathdup("%s/pack-%s%s",
-                                       packdir, item->string, exts[ext]);
+                                       packdir, item->string, exts[ext].name);
                        fname_old = mkpathdup("%s-%s%s",
-                                       packtmp, item->string, exts[ext]);
+                                       packtmp, item->string, exts[ext].name);
                        if (!stat(fname_old, &statbuffer)) {
                                statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
                                chmod(fname_old, statbuffer.st_mode);
+                               exists = 1;
+                       }
+                       if (exists || !exts[ext].optional) {
+                               if (rename(fname_old, fname))
+                                       die_errno(_("renaming '%s' failed"), fname_old);
                        }
-                       if (rename(fname_old, fname))
-                               die_errno(_("renaming '%s' failed"), fname_old);
                        free(fname);
                        free(fname_old);
                }
@@ -333,12 +350,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        /* Remove the "old-" files */
        for_each_string_list_item(item, &names) {
-               for (ext = 0; ext < 2; ext++) {
+               for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
                        char *fname;
                        fname = mkpath("%s/old-%s%s",
                                        packdir,
                                        item->string,
-                                       exts[ext]);
+                                       exts[ext].name);
                        if (remove_path(fname))
                                warning(_("removing '%s' failed"), fname);
                }
index 60048030dd4d05e9938f298002d02afc31528bf1..4fd1c6c51d3ce5af60f2978f3839c0e595d3fece 100644 (file)
@@ -116,25 +116,34 @@ static void update_index_from_diff(struct diff_queue_struct *q,
                struct diff_options *opt, void *data)
 {
        int i;
+       int intent_to_add = *(int *)data;
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filespec *one = q->queue[i]->one;
-               if (one->mode && !is_null_sha1(one->sha1)) {
-                       struct cache_entry *ce;
-                       ce = make_cache_entry(one->mode, one->sha1, one->path,
-                               0, 0);
-                       if (!ce)
-                               die(_("make_cache_entry failed for path '%s'"),
-                                   one->path);
-                       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
-                               ADD_CACHE_OK_TO_REPLACE);
-               } else
+               int is_missing = !(one->mode && !is_null_sha1(one->sha1));
+               struct cache_entry *ce;
+
+               if (is_missing && !intent_to_add) {
                        remove_file_from_cache(one->path);
+                       continue;
+               }
+
+               ce = make_cache_entry(one->mode, one->sha1, one->path,
+                                     0, 0);
+               if (!ce)
+                       die(_("make_cache_entry failed for path '%s'"),
+                           one->path);
+               if (is_missing) {
+                       ce->ce_flags |= CE_INTENT_TO_ADD;
+                       set_object_name_for_intent_to_add_entry(ce);
+               }
+               add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        }
 }
 
 static int read_from_tree(const struct pathspec *pathspec,
-                         unsigned char *tree_sha1)
+                         unsigned char *tree_sha1,
+                         int intent_to_add)
 {
        struct diff_options opt;
 
@@ -142,6 +151,7 @@ static int read_from_tree(const struct pathspec *pathspec,
        copy_pathspec(&opt.pathspec, pathspec);
        opt.output_format = DIFF_FORMAT_CALLBACK;
        opt.format_callback = update_index_from_diff;
+       opt.format_callback_data = &intent_to_add;
 
        if (do_diff_cache(tree_sha1, &opt))
                return 1;
@@ -258,6 +268,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        const char *rev;
        unsigned char sha1[20];
        struct pathspec pathspec;
+       int intent_to_add = 0;
        const struct option options[] = {
                OPT__QUIET(&quiet, N_("be quiet, only report errors")),
                OPT_SET_INT(0, "mixed", &reset_type,
@@ -270,6 +281,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                OPT_SET_INT(0, "keep", &reset_type,
                                N_("reset HEAD but keep local changes"), KEEP),
                OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
+               OPT_BOOL('N', "intent-to-add", &intent_to_add,
+                               N_("record only the fact that removed paths will be added later")),
                OPT_END()
        };
 
@@ -327,6 +340,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                die(_("%s reset is not allowed in a bare repository"),
                    _(reset_type_names[reset_type]));
 
+       if (intent_to_add && reset_type != MIXED)
+               die(_("-N can only be used with --mixed"));
+
        /* Soft reset does not touch the index file nor the working tree
         * at all, but requires them in a good order.  Other resets reset
         * the index file to the tree object we are switching to. */
@@ -338,7 +354,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                int newfd = hold_locked_index(lock, 1);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
-                       if (read_from_tree(&pathspec, sha1))
+                       if (read_from_tree(&pathspec, sha1, intent_to_add))
                                return 1;
                        refresh_index(&the_index, flags, NULL, NULL,
                                      _("Unstaged changes after reset:"));
index 0745e2d05330d21152f2aff720d0de338270aa6d..9f92905379d1801a35bfad87bc05002d76dc23f4 100644 (file)
@@ -3,6 +3,8 @@
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
+#include "pack.h"
+#include "pack-bitmap.h"
 #include "builtin.h"
 #include "log-tree.h"
 #include "graph.h"
@@ -257,6 +259,18 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
        return 0;
 }
 
+static int show_object_fast(
+       const unsigned char *sha1,
+       enum object_type type,
+       int exclude,
+       uint32_t name_hash,
+       struct packed_git *found_pack,
+       off_t found_offset)
+{
+       fprintf(stdout, "%s\n", sha1_to_hex(sha1));
+       return 1;
+}
+
 int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
@@ -265,6 +279,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        int bisect_list = 0;
        int bisect_show_vars = 0;
        int bisect_find_all = 0;
+       int use_bitmap_index = 0;
 
        git_config(git_default_config, NULL);
        init_revisions(&revs, prefix);
@@ -306,6 +321,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        bisect_show_vars = 1;
                        continue;
                }
+               if (!strcmp(arg, "--use-bitmap-index")) {
+                       use_bitmap_index = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--test-bitmap")) {
+                       test_bitmap_walk(&revs);
+                       return 0;
+               }
                usage(rev_list_usage);
 
        }
@@ -333,6 +356,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (bisect_list)
                revs.limited = 1;
 
+       if (use_bitmap_index) {
+               if (revs.count && !revs.left_right && !revs.cherry_mark) {
+                       uint32_t commit_count;
+                       if (!prepare_bitmap_walk(&revs)) {
+                               count_bitmap_commit_list(&commit_count, NULL, NULL, NULL);
+                               printf("%d\n", commit_count);
+                               return 0;
+                       }
+               } else if (revs.tag_objects && revs.tree_objects && revs.blob_objects) {
+                       if (!prepare_bitmap_walk(&revs)) {
+                               traverse_bitmap_commit_list(&show_object_fast);
+                               return 0;
+                       }
+               }
+       }
+
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        if (revs.tree_objects)
index aaeb611a9729938251b1c679e35c446bb737ff4f..45901df37103f37418d7558adb8ace36c8b56aeb 100644 (file)
@@ -547,15 +547,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--default")) {
-                               def = argv[i+1];
-                               i++;
+                               def = argv[++i];
+                               if (!def)
+                                       die("--default requires an argument");
                                continue;
                        }
                        if (!strcmp(arg, "--prefix")) {
-                               prefix = argv[i+1];
+                               prefix = argv[++i];
+                               if (!prefix)
+                                       die("--prefix requires an argument");
                                startup_info->prefix = prefix;
                                output_prefix = 1;
-                               i++;
                                continue;
                        }
                        if (!strcmp(arg, "--revs-only")) {
@@ -738,9 +740,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--resolve-git-dir")) {
-                               const char *gitdir = resolve_gitdir(argv[i+1]);
+                               const char *gitdir = argv[++i];
                                if (!gitdir)
-                                       die("not a gitdir '%s'", argv[i+1]);
+                                       die("--resolve-git-dir requires an argument");
+                               gitdir = resolve_gitdir(gitdir);
+                               if (!gitdir)
+                                       die("not a gitdir '%s'", argv[i]);
                                puts(gitdir);
                                continue;
                        }
index 87659c9fdb749bf8cbf33ce71391fdfe284edd6d..065d88dd05ea65d6e53e617417555399920f8afd 100644 (file)
@@ -89,6 +89,8 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
                OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
                OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
                        N_("option for merge strategy"), option_parse_x),
+               { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key id"),
+                 N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                OPT_END(),
                OPT_END(),
                OPT_END(),
index 3a0e0eaab7d1fd8a298bb2519776abede5c685e2..960634dd0c52f1da689e8a54980e5e81cf2d2f36 100644 (file)
@@ -308,10 +308,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
        for (i = 0; i < active_nr; i++) {
                const struct cache_entry *ce = active_cache[i];
-               if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen))
+               if (!ce_path_match(ce, &pathspec, seen))
                        continue;
                ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
-               list.entry[list.nr].name = ce->name;
+               list.entry[list.nr].name = xstrdup(ce->name);
                list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
                if (list.entry[list.nr++].is_submodule &&
                    !is_staging_gitmodules_ok())
index e3a10d706d406845b74b2cc0c9e9a120be6adce0..d12ad95f3e210f780d95334d59a58e6b9d3b69ad 100644 (file)
@@ -12,6 +12,7 @@
 #include "resolve-undo.h"
 #include "parse-options.h"
 #include "pathspec.h"
+#include "dir.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -274,36 +275,32 @@ static void chmod_path(int flip, const char *path)
        die("git update-index: cannot chmod %cx '%s'", flip, path);
 }
 
-static void update_one(const char *path, const char *prefix, int prefix_length)
+static void update_one(const char *path)
 {
-       const char *p = prefix_path(prefix, prefix_length, path);
-       if (!verify_path(p)) {
+       if (!verify_path(path)) {
                fprintf(stderr, "Ignoring path %s\n", path);
-               goto free_return;
+               return;
        }
        if (mark_valid_only) {
-               if (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG))
+               if (mark_ce_flags(path, CE_VALID, mark_valid_only == MARK_FLAG))
                        die("Unable to mark file %s", path);
-               goto free_return;
+               return;
        }
        if (mark_skip_worktree_only) {
-               if (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
+               if (mark_ce_flags(path, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
                        die("Unable to mark file %s", path);
-               goto free_return;
+               return;
        }
 
        if (force_remove) {
-               if (remove_file_from_cache(p))
+               if (remove_file_from_cache(path))
                        die("git update-index: unable to remove %s", path);
                report("remove '%s'", path);
-               goto free_return;
+               return;
        }
-       if (process_path(p))
+       if (process_path(path))
                die("Unable to process path %s", path);
        report("add '%s'", path);
- free_return:
-       if (p < path || p > path + strlen(path))
-               free((char *)p);
 }
 
 static void read_index_info(int line_termination)
@@ -563,8 +560,9 @@ static int do_reupdate(int ac, const char **av,
                const struct cache_entry *ce = active_cache[pos];
                struct cache_entry *old = NULL;
                int save_nr;
+               char *path;
 
-               if (ce_stage(ce) || !ce_path_match(ce, &pathspec))
+               if (ce_stage(ce) || !ce_path_match(ce, &pathspec, NULL))
                        continue;
                if (has_head)
                        old = read_one_ent(NULL, head_sha1,
@@ -579,7 +577,9 @@ static int do_reupdate(int ac, const char **av,
                 * or worse yet 'allow_replace', active_nr may decrease.
                 */
                save_nr = active_nr;
-               update_one(ce->name + prefix_length, prefix, prefix_length);
+               path = xstrdup(ce->name);
+               update_one(path);
+               free(path);
                if (save_nr != active_nr)
                        goto redo;
        }
@@ -836,11 +836,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 
                        setup_work_tree();
                        p = prefix_path(prefix, prefix_length, path);
-                       update_one(p, NULL, 0);
+                       update_one(p);
                        if (set_executable_bit)
                                chmod_path(set_executable_bit, p);
-                       if (p < path || p > path + strlen(path))
-                               free((char *)p);
+                       free((char *)p);
                        ctx.argc--;
                        ctx.argv++;
                        break;
@@ -879,11 +878,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                                strbuf_swap(&buf, &nbuf);
                        }
                        p = prefix_path(prefix, prefix_length, buf.buf);
-                       update_one(p, NULL, 0);
+                       update_one(p);
                        if (set_executable_bit)
                                chmod_path(set_executable_bit, p);
-                       if (p < buf.buf || p > buf.buf + buf.len)
-                               free((char *)p);
+                       free((char *)p);
                }
                strbuf_release(&nbuf);
                strbuf_release(&buf);
diff --git a/cache.h b/cache.h
index dc040fb1aa99b7970e85e5b175e60f862ff6a74a..bb0097e929b1411458ea9194bf3d7c0c56fb2f5a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -3,7 +3,7 @@
 
 #include "git-compat-util.h"
 #include "strbuf.h"
-#include "hash.h"
+#include "hashmap.h"
 #include "advice.h"
 #include "gettext.h"
 #include "convert.h"
@@ -130,12 +130,12 @@ struct stat_data {
 };
 
 struct cache_entry {
+       struct hashmap_entry ent;
        struct stat_data ce_stat_data;
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned char sha1[20];
-       struct cache_entry *next;
        char name[FLEX_ARRAY]; /* more */
 };
 
@@ -159,7 +159,6 @@ struct cache_entry {
 #define CE_ADDED             (1 << 19)
 
 #define CE_HASHED            (1 << 20)
-#define CE_UNHASHED          (1 << 21)
 #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
 #define CE_CONFLICTED        (1 << 23)
 
@@ -195,17 +194,18 @@ struct pathspec;
  * Copy the sha1 and stat state of a cache entry from one to
  * another. But we never change the name, or the hash state!
  */
-#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
 static inline void copy_cache_entry(struct cache_entry *dst,
                                    const struct cache_entry *src)
 {
-       unsigned int state = dst->ce_flags & CE_STATE_MASK;
+       unsigned int state = dst->ce_flags & CE_HASHED;
 
        /* Don't copy hash chain and name */
-       memcpy(dst, src, offsetof(struct cache_entry, next));
+       memcpy(&dst->ce_stat_data, &src->ce_stat_data,
+                       offsetof(struct cache_entry, name) -
+                       offsetof(struct cache_entry, ce_stat_data));
 
        /* Restore the hash state */
-       dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
+       dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
 }
 
 static inline unsigned create_ce_flags(unsigned stage)
@@ -277,8 +277,8 @@ struct index_state {
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
-       struct hash_table name_hash;
-       struct hash_table dir_hash;
+       struct hashmap name_hash;
+       struct hashmap dir_hash;
 };
 
 extern struct index_state the_index;
@@ -316,7 +316,6 @@ extern void free_name_hash(struct index_state *istate);
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
 #define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
 #define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
-#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
 #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
 #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
 #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
@@ -434,6 +433,7 @@ extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
 extern int init_db(const char *template_dir, unsigned int flags);
 
 extern void sanitize_stdfds(void);
+extern int daemonize(void);
 
 #define alloc_nr(x) (((x)+16)*3/2)
 
@@ -467,7 +467,6 @@ extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
 extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
 extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
-extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
@@ -484,11 +483,11 @@ extern int remove_file_from_index(struct index_state *, const char *path);
 #define ADD_CACHE_IGNORE_ERRORS        4
 #define ADD_CACHE_IGNORE_REMOVAL 8
 #define ADD_CACHE_INTENT 16
-#define ADD_CACHE_IMPLICIT_DOT 32      /* internal to "git add -u/-A" */
 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
-extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
+extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
 extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
+extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
 extern int index_name_is_other(const struct index_state *, const char *, int);
 extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
 
@@ -498,11 +497,13 @@ extern void *read_blob_data_from_index(struct index_state *, const char *, unsig
 #define CE_MATCH_RACY_IS_DIRTY         02
 /* do stat comparison even if CE_SKIP_WORKTREE is true */
 #define CE_MATCH_IGNORE_SKIP_WORKTREE  04
+/* ignore non-existent files during stat update  */
+#define CE_MATCH_IGNORE_MISSING                0x08
+/* enable stat refresh */
+#define CE_MATCH_REFRESH               0x10
 extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
-extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
-
 #define HASH_WRITE_OBJECT 1
 #define HASH_FORMAT_CHECK 2
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
@@ -808,6 +809,7 @@ extern int hash_sha1_file(const void *buf, unsigned long len, const char *type,
 extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
+extern int git_open_noatime(const char *name);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
@@ -1146,6 +1148,12 @@ extern int update_server_info(int);
 #define CONFIG_INVALID_PATTERN 6
 #define CONFIG_GENERIC_ERROR 7
 
+struct git_config_source {
+       unsigned int use_stdin:1;
+       const char *file;
+       const char *blob;
+};
+
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
@@ -1155,8 +1163,7 @@ extern void git_config_push_parameter(const char *text);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
 extern int git_config(config_fn_t fn, void *);
 extern int git_config_with_options(config_fn_t fn, void *,
-                                  const char *filename,
-                                  const char *blob_ref,
+                                  struct git_config_source *config_source,
                                   int respect_includes);
 extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
 extern int git_parse_ulong(const char *, unsigned long *);
index 3b92c44880228a94f71a428b0f11bb1caf693c67..24ca7e2334b68e06afd24051ad1aafcc129bcba4 100644 (file)
 static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
-       struct combine_diff_path *p;
-       int i;
+       struct combine_diff_path *p, **tail = &curr;
+       int i, cmp;
 
        if (!n) {
-               struct combine_diff_path *list = NULL, **tail = &list;
                for (i = 0; i < q->nr; i++) {
                        int len;
                        const char *path;
@@ -31,7 +30,6 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                        p->path = (char *) &(p->parent[num_parent]);
                        memcpy(p->path, path, len);
                        p->path[len] = 0;
-                       p->len = len;
                        p->next = NULL;
                        memset(p->parent, 0,
                               sizeof(p->parent[0]) * num_parent);
@@ -44,31 +42,37 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                        *tail = p;
                        tail = &p->next;
                }
-               return list;
+               return curr;
        }
 
-       for (p = curr; p; p = p->next) {
-               int found = 0;
-               if (!p->len)
+       /*
+        * paths in curr (linked list) and q->queue[] (array) are
+        * both sorted in the tree order.
+        */
+       i = 0;
+       while ((p = *tail) != NULL) {
+               cmp = ((i >= q->nr)
+                      ? -1 : strcmp(p->path, q->queue[i]->two->path));
+
+               if (cmp < 0) {
+                       /* p->path not in q->queue[]; drop it */
+                       *tail = p->next;
+                       free(p);
                        continue;
-               for (i = 0; i < q->nr; i++) {
-                       const char *path;
-                       int len;
+               }
 
-                       if (diff_unmodified_pair(q->queue[i]))
-                               continue;
-                       path = q->queue[i]->two->path;
-                       len = strlen(path);
-                       if (len == p->len && !memcmp(path, p->path, len)) {
-                               found = 1;
-                               hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
-                               p->parent[n].mode = q->queue[i]->one->mode;
-                               p->parent[n].status = q->queue[i]->status;
-                               break;
-                       }
+               if (cmp > 0) {
+                       /* q->queue[i] not in p->path; skip it */
+                       i++;
+                       continue;
                }
-               if (!found)
-                       p->len = 0;
+
+               hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
+               p->parent[n].mode = q->queue[i]->one->mode;
+               p->parent[n].status = q->queue[i]->status;
+
+               tail = &p->next;
+               i++;
        }
        return curr;
 }
@@ -1219,8 +1223,6 @@ void show_combined_diff(struct combine_diff_path *p,
 {
        struct diff_options *opt = &rev->diffopt;
 
-       if (!p->len)
-               return;
        if (opt->output_format & (DIFF_FORMAT_RAW |
                                  DIFF_FORMAT_NAME |
                                  DIFF_FORMAT_NAME_STATUS))
@@ -1284,17 +1286,21 @@ static void handle_combined_callback(struct diff_options *opt,
        q.queue = xcalloc(num_paths, sizeof(struct diff_filepair *));
        q.alloc = num_paths;
        q.nr = num_paths;
-       for (i = 0, p = paths; p; p = p->next) {
-               if (!p->len)
-                       continue;
+       for (i = 0, p = paths; p; p = p->next)
                q.queue[i++] = combined_pair(p, num_parent);
-       }
        opt->format_callback(&q, opt, opt->format_callback_data);
        for (i = 0; i < num_paths; i++)
                free_combined_pair(q.queue[i]);
        free(q.queue);
 }
 
+static const char *path_path(void *obj)
+{
+       struct combine_diff_path *path = (struct combine_diff_path *)obj;
+
+       return path->path;
+}
+
 void diff_tree_combined(const unsigned char *sha1,
                        const struct sha1_array *parents,
                        int dense,
@@ -1310,6 +1316,8 @@ void diff_tree_combined(const unsigned char *sha1,
        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
        DIFF_OPT_SET(&diffopts, RECURSIVE);
        DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
+       /* tell diff_tree to emit paths in sorted (=tree) order */
+       diffopts.orderfile = NULL;
 
        show_log_first = !!rev->loginfo && !rev->no_commit_id;
        needsep = 0;
@@ -1335,22 +1343,46 @@ void diff_tree_combined(const unsigned char *sha1,
                                printf("%s%c", diff_line_prefix(opt),
                                       opt->line_termination);
                }
+
+               /* if showing diff, show it in requested order */
+               if (diffopts.output_format != DIFF_FORMAT_NO_OUTPUT &&
+                   opt->orderfile) {
+                       diffcore_order(opt->orderfile);
+               }
+
                diff_flush(&diffopts);
        }
 
-       /* find out surviving paths */
-       for (num_paths = 0, p = paths; p; p = p->next) {
-               if (p->len)
-                       num_paths++;
+       /* find out number of surviving paths */
+       for (num_paths = 0, p = paths; p; p = p->next)
+               num_paths++;
+
+       /* order paths according to diffcore_order */
+       if (opt->orderfile && num_paths) {
+               struct obj_order *o;
+
+               o = xmalloc(sizeof(*o) * num_paths);
+               for (i = 0, p = paths; p; p = p->next, i++)
+                       o[i].obj = p;
+               order_objects(opt->orderfile, path_path, o, num_paths);
+               for (i = 0; i < num_paths - 1; i++) {
+                       p = o[i].obj;
+                       p->next = o[i+1].obj;
+               }
+
+               p = o[num_paths-1].obj;
+               p->next = NULL;
+               paths = o[0].obj;
+               free(o);
        }
+
+
        if (num_paths) {
                if (opt->output_format & (DIFF_FORMAT_RAW |
                                          DIFF_FORMAT_NAME |
                                          DIFF_FORMAT_NAME_STATUS)) {
-                       for (p = paths; p; p = p->next) {
-                               if (p->len)
-                                       show_raw_diff(p, num_parent, rev);
-                       }
+                       for (p = paths; p; p = p->next)
+                               show_raw_diff(p, num_parent, rev);
                        needsep = 1;
                }
                else if (opt->output_format &
@@ -1363,11 +1395,9 @@ void diff_tree_combined(const unsigned char *sha1,
                        if (needsep)
                                printf("%s%c", diff_line_prefix(opt),
                                       opt->line_termination);
-                       for (p = paths; p; p = p->next) {
-                               if (p->len)
-                                       show_patch_diff(p, num_parent, dense,
-                                                       0, rev);
-                       }
+                       for (p = paths; p; p = p->next)
+                               show_patch_diff(p, num_parent, dense,
+                                               0, rev);
                }
        }
 
index 5061214f73d2d51cfd1d49b97b4e69be913928bd..120c6c1d3755963285651423cf4b5c791ae64be0 100644 (file)
@@ -17,7 +17,20 @@ static inline uint32_t default_swab32(uint32_t val)
                ((val & 0x000000ff) << 24));
 }
 
+static inline uint64_t default_bswap64(uint64_t val)
+{
+       return (((val & (uint64_t)0x00000000000000ffULL) << 56) |
+               ((val & (uint64_t)0x000000000000ff00ULL) << 40) |
+               ((val & (uint64_t)0x0000000000ff0000ULL) << 24) |
+               ((val & (uint64_t)0x00000000ff000000ULL) <<  8) |
+               ((val & (uint64_t)0x000000ff00000000ULL) >>  8) |
+               ((val & (uint64_t)0x0000ff0000000000ULL) >> 24) |
+               ((val & (uint64_t)0x00ff000000000000ULL) >> 40) |
+               ((val & (uint64_t)0xff00000000000000ULL) >> 56));
+}
+
 #undef bswap32
+#undef bswap64
 
 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
 
@@ -32,15 +45,42 @@ static inline uint32_t git_bswap32(uint32_t x)
        return result;
 }
 
+#define bswap64 git_bswap64
+#if defined(__x86_64__)
+static inline uint64_t git_bswap64(uint64_t x)
+{
+       uint64_t result;
+       if (__builtin_constant_p(x))
+               result = default_bswap64(x);
+       else
+               __asm__("bswap %q0" : "=r" (result) : "0" (x));
+       return result;
+}
+#else
+static inline uint64_t git_bswap64(uint64_t x)
+{
+       union { uint64_t i64; uint32_t i32[2]; } tmp, result;
+       if (__builtin_constant_p(x))
+               result.i64 = default_bswap64(x);
+       else {
+               tmp.i64 = x;
+               result.i32[0] = git_bswap32(tmp.i32[1]);
+               result.i32[1] = git_bswap32(tmp.i32[0]);
+       }
+       return result.i64;
+}
+#endif
+
 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
 
 #include <stdlib.h>
 
 #define bswap32(x) _byteswap_ulong(x)
+#define bswap64(x) _byteswap_uint64(x)
 
 #endif
 
-#ifdef bswap32
+#if defined(bswap32)
 
 #undef ntohl
 #undef htonl
@@ -48,3 +88,73 @@ static inline uint32_t git_bswap32(uint32_t x)
 #define htonl(x) bswap32(x)
 
 #endif
+
+#if defined(bswap64)
+
+#undef ntohll
+#undef htonll
+#define ntohll(x) bswap64(x)
+#define htonll(x) bswap64(x)
+
+#else
+
+#undef ntohll
+#undef htonll
+
+#if !defined(__BYTE_ORDER)
+# if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+#  define __BYTE_ORDER BYTE_ORDER
+#  define __LITTLE_ENDIAN LITTLE_ENDIAN
+#  define __BIG_ENDIAN BIG_ENDIAN
+# endif
+#endif
+
+#if !defined(__BYTE_ORDER)
+# error "Cannot determine endianness"
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+# define ntohll(n) (n)
+# define htonll(n) (n)
+#else
+# define ntohll(n) default_bswap64(n)
+# define htonll(n) default_bswap64(n)
+#endif
+
+#endif
+
+/*
+ * Performance might be improved if the CPU architecture is OK with
+ * unaligned 32-bit loads and a fast ntohl() is available.
+ * Otherwise fall back to byte loads and shifts which is portable,
+ * and is faster on architectures with memory alignment issues.
+ */
+
+#if defined(__i386__) || defined(__x86_64__) || \
+    defined(_M_IX86) || defined(_M_X64) || \
+    defined(__ppc__) || defined(__ppc64__) || \
+    defined(__powerpc__) || defined(__powerpc64__) || \
+    defined(__s390__) || defined(__s390x__)
+
+#define get_be16(p)    ntohs(*(unsigned short *)(p))
+#define get_be32(p)    ntohl(*(unsigned int *)(p))
+#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
+
+#else
+
+#define get_be16(p)    ( \
+       (*((unsigned char *)(p) + 0) << 8) | \
+       (*((unsigned char *)(p) + 1) << 0) )
+#define get_be32(p)    ( \
+       (*((unsigned char *)(p) + 0) << 24) | \
+       (*((unsigned char *)(p) + 1) << 16) | \
+       (*((unsigned char *)(p) + 2) <<  8) | \
+       (*((unsigned char *)(p) + 3) <<  0) )
+#define put_be32(p, v) do { \
+       unsigned int __v = (v); \
+       *((unsigned char *)(p) + 0) = __v >> 24; \
+       *((unsigned char *)(p) + 1) = __v >> 16; \
+       *((unsigned char *)(p) + 2) = __v >>  8; \
+       *((unsigned char *)(p) + 3) = __v >>  0; } while (0)
+
+#endif
index d969a5aefc2bca92938d3fd7f6a507e884ce2b7f..6821cef00a0da56fbdadbd34d179ba5bd125c3a6 100644 (file)
--- a/config.c
+++ b/config.c
@@ -21,6 +21,7 @@ struct config_source {
                } buf;
        } u;
        const char *name;
+       const char *path;
        int die_on_error;
        int linenr;
        int eof;
@@ -84,8 +85,12 @@ static int handle_path_include(const char *path, struct config_include_data *inc
 {
        int ret = 0;
        struct strbuf buf = STRBUF_INIT;
-       char *expanded = expand_user_path(path);
+       char *expanded;
 
+       if (!path)
+               return config_error_nonbool("include.path");
+
+       expanded = expand_user_path(path);
        if (!expanded)
                return error("Could not expand include path '%s'", path);
        path = expanded;
@@ -97,12 +102,12 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        if (!is_absolute_path(path)) {
                char *slash;
 
-               if (!cf || !cf->name)
+               if (!cf || !cf->path)
                        return error("relative config includes must come from files");
 
-               slash = find_last_dir_sep(cf->name);
+               slash = find_last_dir_sep(cf->path);
                if (slash)
-                       strbuf_add(&buf, cf->name, slash - cf->name + 1);
+                       strbuf_add(&buf, cf->path, slash - cf->path + 1);
                strbuf_addstr(&buf, path);
                path = buf.buf;
        }
@@ -663,20 +668,7 @@ static int git_default_core_config(const char *var, const char *value)
                trust_ctime = git_config_bool(var, value);
                return 0;
        }
-       if (!strcmp(var, "core.statinfo") ||
-           !strcmp(var, "core.checkstat")) {
-               /*
-                * NEEDSWORK: statinfo was a typo in v1.8.2 that has
-                * never been advertised.  we will remove it at Git
-                * 2.0 boundary.
-                */
-               if (!strcmp(var, "core.statinfo")) {
-                       static int warned;
-                       if (!warned++) {
-                               warning("'core.statinfo' will be removed in Git 2.0; "
-                                       "use 'core.checkstat' instead.");
-                       }
-               }
+       if (!strcmp(var, "core.checkstat")) {
                if (!strcasecmp(value, "default"))
                        check_stat = 1;
                else if (!strcasecmp(value, "minimal"))
@@ -1030,24 +1022,35 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
        return ret;
 }
 
-int git_config_from_file(config_fn_t fn, const char *filename, void *data)
+static int do_config_from_file(config_fn_t fn,
+               const char *name, const char *path, FILE *f, void *data)
 {
-       int ret;
-       FILE *f = fopen(filename, "r");
+       struct config_source top;
 
-       ret = -1;
-       if (f) {
-               struct config_source top;
+       top.u.file = f;
+       top.name = name;
+       top.path = path;
+       top.die_on_error = 1;
+       top.do_fgetc = config_file_fgetc;
+       top.do_ungetc = config_file_ungetc;
+       top.do_ftell = config_file_ftell;
 
-               top.u.file = f;
-               top.name = filename;
-               top.die_on_error = 1;
-               top.do_fgetc = config_file_fgetc;
-               top.do_ungetc = config_file_ungetc;
-               top.do_ftell = config_file_ftell;
+       return do_config_from(&top, fn, data);
+}
 
-               ret = do_config_from(&top, fn, data);
+static int git_config_from_stdin(config_fn_t fn, void *data)
+{
+       return do_config_from_file(fn, "<stdin>", NULL, stdin, data);
+}
 
+int git_config_from_file(config_fn_t fn, const char *filename, void *data)
+{
+       int ret = -1;
+       FILE *f;
+
+       f = fopen(filename, "r");
+       if (f) {
+               ret = do_config_from_file(fn, filename, filename, f, data);
                fclose(f);
        }
        return ret;
@@ -1062,6 +1065,7 @@ int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
        top.u.buf.len = len;
        top.u.buf.pos = 0;
        top.name = name;
+       top.path = NULL;
        top.die_on_error = 0;
        top.do_fgetc = config_buf_fgetc;
        top.do_ungetc = config_buf_ungetc;
@@ -1170,8 +1174,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 }
 
 int git_config_with_options(config_fn_t fn, void *data,
-                           const char *filename,
-                           const char *blob_ref,
+                           struct git_config_source *config_source,
                            int respect_includes)
 {
        char *repo_config = NULL;
@@ -1189,10 +1192,12 @@ int git_config_with_options(config_fn_t fn, void *data,
         * If we have a specific filename, use it. Otherwise, follow the
         * regular lookup sequence.
         */
-       if (filename)
-               return git_config_from_file(fn, filename, data);
-       else if (blob_ref)
-               return git_config_from_blob_ref(fn, blob_ref, data);
+       if (config_source && config_source->use_stdin)
+               return git_config_from_stdin(fn, data);
+       else if (config_source && config_source->file)
+               return git_config_from_file(fn, config_source->file, data);
+       else if (config_source && config_source->blob)
+               return git_config_from_blob_ref(fn, config_source->blob, data);
 
        repo_config = git_pathdup("config");
        ret = git_config_early(fn, data, repo_config);
@@ -1203,7 +1208,7 @@ int git_config_with_options(config_fn_t fn, void *data,
 
 int git_config(config_fn_t fn, void *data)
 {
-       return git_config_with_options(fn, data, NULL, NULL, 1);
+       return git_config_with_options(fn, data, NULL, 1);
 }
 
 /*
index 9525343fcd34ce5009fcfd9b2bf2c6356282e259..87de809d23146c2cee818555028cf8a24dc6c908 100644 (file)
@@ -1221,14 +1221,20 @@ _git_difftool ()
        __git_complete_revlist_file
 }
 
+__git_fetch_recurse_submodules="yes on-demand no"
+
 __git_fetch_options="
        --quiet --verbose --append --upload-pack --force --keep --depth=
-       --tags --no-tags --all --prune --dry-run
+       --tags --no-tags --all --prune --dry-run --recurse-submodules=
 "
 
 _git_fetch ()
 {
        case "$cur" in
+       --recurse-submodules=*)
+               __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
+               return
+               ;;
        --*)
                __gitcomp "$__git_fetch_options"
                return
@@ -1583,6 +1589,10 @@ _git_pull ()
        __git_complete_strategy && return
 
        case "$cur" in
+       --recurse-submodules=*)
+               __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
+               return
+               ;;
        --*)
                __gitcomp "
                        --rebase --no-rebase
@@ -1595,6 +1605,8 @@ _git_pull ()
        __git_complete_remote_or_refspec
 }
 
+__git_push_recurse_submodules="check on-demand"
+
 _git_push ()
 {
        case "$prev" in
@@ -1607,10 +1619,15 @@ _git_push ()
                __gitcomp_nl "$(__git_remotes)" "" "${cur##--repo=}"
                return
                ;;
+       --recurse-submodules=*)
+               __gitcomp "$__git_push_recurse_submodules" "" "${cur##--recurse-submodules=}"
+               return
+               ;;
        --*)
                __gitcomp "
                        --all --mirror --tags --dry-run --force --verbose
                        --receive-pack= --repo= --set-upstream
+                       --recurse-submodules=
                "
                return
                ;;
index a4ed4c3c62f0d5abcee36000a6c3a8f43dc02112..d7e97bbc76c27f61e83a5328831fd6889b22e662 100755 (executable)
@@ -10,6 +10,7 @@ is rather slow but allows you to resurrect other people's topic
 branches."
 
 OPTIONS_KEEPDASHDASH=
+OPTIONS_STUCKLONG=
 OPTIONS_SPEC="\
 git resurrect $USAGE
 --
index 503e03906c5c7f921e4d67a53835f771ea695d0c..eba12556848e975cd6f1a55ae25760823e90cdc8 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1056,11 +1056,6 @@ static void drop_privileges(struct credentials *cred)
        /* nothing */
 }
 
-static void daemonize(void)
-{
-       die("--detach not supported on this platform");
-}
-
 static struct credentials *prepare_credentials(const char *user_name,
     const char *group_name)
 {
@@ -1102,24 +1097,6 @@ static struct credentials *prepare_credentials(const char *user_name,
 
        return &c;
 }
-
-static void daemonize(void)
-{
-       switch (fork()) {
-               case 0:
-                       break;
-               case -1:
-                       die_errno("fork failed");
-               default:
-                       exit(0);
-       }
-       if (setsid() == -1)
-               die_errno("setsid failed");
-       close(0);
-       close(1);
-       close(2);
-       sanitize_stdfds();
-}
 #endif
 
 static void store_pid(const char *path)
@@ -1333,9 +1310,10 @@ int main(int argc, char **argv)
        if (inetd_mode || serve_mode)
                return execute();
 
-       if (detach)
-               daemonize();
-       else
+       if (detach) {
+               if (daemonize())
+                       die("--detach not supported on this platform");
+       } else
                sanitize_stdfds();
 
        if (pid_file)
index 346cac651da725af07a304194d8d8e04b2f080b7..044872935c30d84affb1955a7ae03c0986c90bda 100644 (file)
@@ -11,6 +11,7 @@
 #include "unpack-trees.h"
 #include "refs.h"
 #include "submodule.h"
+#include "dir.h"
 
 /*
  * diff-files
@@ -90,9 +91,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
        unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
                              ? CE_MATCH_RACY_IS_DIRTY : 0);
 
-       if (option & DIFF_SILENT_ON_REMOVED)
-               handle_deprecated_show_diff_q(&revs->diffopt);
-
        diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
 
        if (diff_unmerged_stage < 0)
@@ -108,7 +106,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                if (diff_can_quit_early(&revs->diffopt))
                        break;
 
-               if (!ce_path_match(ce, &revs->prune_data))
+               if (!ce_path_match(ce, &revs->prune_data, NULL))
                        continue;
 
                if (ce_stage(ce)) {
@@ -124,7 +122,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        dpath->path = (char *) &(dpath->parent[5]);
 
                        dpath->next = NULL;
-                       dpath->len = path_len;
                        memcpy(dpath->path, ce->name, path_len);
                        dpath->path[path_len] = '\0';
                        hashclr(dpath->sha1);
@@ -326,7 +323,6 @@ static int show_modified(struct rev_info *revs,
                p = xmalloc(combine_diff_path_size(2, pathlen));
                p->path = (char *) &p->parent[2];
                p->next = NULL;
-               p->len = pathlen;
                memcpy(p->path, new->name, pathlen);
                p->path[pathlen] = 0;
                p->mode = mode;
@@ -438,7 +434,7 @@ static int oneway_diff(const struct cache_entry * const *src,
        if (tree == o->df_conflict_entry)
                tree = NULL;
 
-       if (ce_path_match(idx ? idx : tree, &revs->prune_data)) {
+       if (ce_path_match(idx ? idx : tree, &revs->prune_data, NULL)) {
                do_oneway_diff(o, idx, tree);
                if (diff_can_quit_early(&revs->diffopt)) {
                        o->exiting_early = 1;
index 33e5982a1c61a5726580f40df0a07dadc7e2aaf1..8e10bff30e5269687e67866c2d3c66d62e3bb883 100644 (file)
@@ -186,7 +186,6 @@ void diff_no_index(struct rev_info *revs,
                   const char *prefix)
 {
        int i, prefixlen;
-       unsigned deprecated_show_diff_q_option_used = 0;
        const char *paths[2];
 
        diff_setup(&revs->diffopt);
@@ -194,10 +193,6 @@ void diff_no_index(struct rev_info *revs,
                int j;
                if (!strcmp(argv[i], "--no-index"))
                        i++;
-               else if (!strcmp(argv[i], "-q")) {
-                       deprecated_show_diff_q_option_used = 1;
-                       i++;
-               }
                else if (!strcmp(argv[i], "--"))
                        i++;
                else {
@@ -230,9 +225,6 @@ void diff_no_index(struct rev_info *revs,
        revs->max_count = -2;
        diff_setup_done(&revs->diffopt);
 
-       if (deprecated_show_diff_q_option_used)
-               handle_deprecated_show_diff_q(&revs->diffopt);
-
        setup_diff_pager(&revs->diffopt);
        DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
 
diff --git a/diff.c b/diff.c
index 8e4a6a910519b2bb6d6a0337682ab402f2650997..7c59bfe2d08edd8fe0f13b5487c1af34043d7ef1 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3599,14 +3599,6 @@ static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt)
        return 0;
 }
 
-/* Used only by "diff-files" and "diff --no-index" */
-void handle_deprecated_show_diff_q(struct diff_options *opt)
-{
-       warning("'diff -q' and 'diff-files -q' are deprecated.");
-       warning("Use 'diff --diff-filter=d' instead to ignore deleted filepairs.");
-       parse_diff_filter_opt("d", opt);
-}
-
 static void enable_patch_output(int *fmt) {
        *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
        *fmt |= DIFF_FORMAT_PATCH;
@@ -4697,6 +4689,38 @@ static int diff_filespec_is_identical(struct diff_filespec *one,
        return !memcmp(one->data, two->data, one->size);
 }
 
+static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
+{
+       if (p->done_skip_stat_unmatch)
+               return p->skip_stat_unmatch_result;
+
+       p->done_skip_stat_unmatch = 1;
+       p->skip_stat_unmatch_result = 0;
+       /*
+        * 1. Entries that come from stat info dirtiness
+        *    always have both sides (iow, not create/delete),
+        *    one side of the object name is unknown, with
+        *    the same mode and size.  Keep the ones that
+        *    do not match these criteria.  They have real
+        *    differences.
+        *
+        * 2. At this point, the file is known to be modified,
+        *    with the same mode and size, and the object
+        *    name of one side is unknown.  Need to inspect
+        *    the identical contents.
+        */
+       if (!DIFF_FILE_VALID(p->one) || /* (1) */
+           !DIFF_FILE_VALID(p->two) ||
+           (p->one->sha1_valid && p->two->sha1_valid) ||
+           (p->one->mode != p->two->mode) ||
+           diff_populate_filespec(p->one, 1) ||
+           diff_populate_filespec(p->two, 1) ||
+           (p->one->size != p->two->size) ||
+           !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+               p->skip_stat_unmatch_result = 1;
+       return p->skip_stat_unmatch_result;
+}
+
 static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
 {
        int i;
@@ -4707,27 +4731,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
 
-               /*
-                * 1. Entries that come from stat info dirtiness
-                *    always have both sides (iow, not create/delete),
-                *    one side of the object name is unknown, with
-                *    the same mode and size.  Keep the ones that
-                *    do not match these criteria.  They have real
-                *    differences.
-                *
-                * 2. At this point, the file is known to be modified,
-                *    with the same mode and size, and the object
-                *    name of one side is unknown.  Need to inspect
-                *    the identical contents.
-                */
-               if (!DIFF_FILE_VALID(p->one) || /* (1) */
-                   !DIFF_FILE_VALID(p->two) ||
-                   (p->one->sha1_valid && p->two->sha1_valid) ||
-                   (p->one->mode != p->two->mode) ||
-                   diff_populate_filespec(p->one, 1) ||
-                   diff_populate_filespec(p->two, 1) ||
-                   (p->one->size != p->two->size) ||
-                   !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+               if (diff_filespec_check_stat_unmatch(p))
                        diff_q(&outq, p);
                else {
                        /*
@@ -4890,6 +4894,7 @@ void diff_change(struct diff_options *options,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
 {
        struct diff_filespec *one, *two;
+       struct diff_filepair *p;
 
        if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
            is_submodule_ignored(concatpath, options))
@@ -4916,10 +4921,16 @@ void diff_change(struct diff_options *options,
        fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
        one->dirty_submodule = old_dirty_submodule;
        two->dirty_submodule = new_dirty_submodule;
+       p = diff_queue(&diff_queued_diff, one, two);
 
-       diff_queue(&diff_queued_diff, one, two);
-       if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
-               DIFF_OPT_SET(options, HAS_CHANGES);
+       if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
+               return;
+
+       if (DIFF_OPT_TST(options, QUICK) && options->skip_stat_unmatch &&
+           !diff_filespec_check_stat_unmatch(p))
+               return;
+
+       DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
 struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
diff --git a/diff.h b/diff.h
index ce123fa06fee6fb6e5e76d6a92ea8284a5bbbcf1..a24a767db78d8e864175cf342f03fc48e159e06e 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -198,7 +198,6 @@ extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
 
 struct combine_diff_path {
        struct combine_diff_path *next;
-       int len;
        char *path;
        unsigned int mode;
        unsigned char sha1[20];
@@ -346,8 +345,6 @@ extern int parse_rename_score(const char **cp_p);
 
 extern long parse_algorithm_value(const char *value);
 
-extern void handle_deprecated_show_diff_q(struct diff_options *);
-
 extern int print_stat_summary(FILE *fp, int files,
                              int insertions, int deletions);
 extern void setup_diff_pager(struct diff_options *);
index fe7f1f4647a16a5882cc4c1e63738c7faa4bc677..1bfcc39f904898b488b5ea0dad1ef3bcb81466ce 100644 (file)
@@ -57,12 +57,6 @@ static void prepare_order(const char *orderfile)
        }
 }
 
-struct pair_order {
-       struct diff_filepair *pair;
-       int orig_order;
-       int order;
-};
-
 static int match_order(const char *path)
 {
        int i;
@@ -84,35 +78,54 @@ static int match_order(const char *path)
        return order_cnt;
 }
 
-static int compare_pair_order(const void *a_, const void *b_)
+static int compare_objs_order(const void *a_, const void *b_)
 {
-       struct pair_order const *a, *b;
-       a = (struct pair_order const *)a_;
-       b = (struct pair_order const *)b_;
+       struct obj_order const *a, *b;
+       a = (struct obj_order const *)a_;
+       b = (struct obj_order const *)b_;
        if (a->order != b->order)
                return a->order - b->order;
        return a->orig_order - b->orig_order;
 }
 
+void order_objects(const char *orderfile, obj_path_fn_t obj_path,
+                  struct obj_order *objs, int nr)
+{
+       int i;
+
+       if (!nr)
+               return;
+
+       prepare_order(orderfile);
+       for (i = 0; i < nr; i++) {
+               objs[i].orig_order = i;
+               objs[i].order = match_order(obj_path(objs[i].obj));
+       }
+       qsort(objs, nr, sizeof(*objs), compare_objs_order);
+}
+
+static const char *pair_pathtwo(void *obj)
+{
+       struct diff_filepair *pair = (struct diff_filepair *)obj;
+
+       return pair->two->path;
+}
+
 void diffcore_order(const char *orderfile)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
-       struct pair_order *o;
+       struct obj_order *o;
        int i;
 
        if (!q->nr)
                return;
 
        o = xmalloc(sizeof(*o) * q->nr);
-       prepare_order(orderfile);
-       for (i = 0; i < q->nr; i++) {
-               o[i].pair = q->queue[i];
-               o[i].orig_order = i;
-               o[i].order = match_order(o[i].pair->two->path);
-       }
-       qsort(o, q->nr, sizeof(*o), compare_pair_order);
        for (i = 0; i < q->nr; i++)
-               q->queue[i] = o[i].pair;
+               o[i].obj = q->queue[i];
+       order_objects(orderfile, pair_pathtwo, o, q->nr);
+       for (i = 0; i < q->nr; i++)
+               q->queue[i] = o[i].obj;
        free(o);
        return;
 }
index 6c7a72fbe74e8dd6fca07d2555cdea62c16b8b1e..9b4f068eb390d9b9d53bdd91f8983432f6ec4563 100644 (file)
@@ -4,7 +4,7 @@
 #include "cache.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "hash.h"
+#include "hashmap.h"
 #include "progress.h"
 
 /* Table of rename/copy destinations */
@@ -243,137 +243,82 @@ static int score_compare(const void *a_, const void *b_)
 }
 
 struct file_similarity {
-       int src_dst, index;
+       struct hashmap_entry entry;
+       int index;
        struct diff_filespec *filespec;
-       struct file_similarity *next;
 };
 
-static int find_identical_files(struct file_similarity *src,
-                               struct file_similarity *dst,
+static unsigned int hash_filespec(struct diff_filespec *filespec)
+{
+       unsigned int hash;
+       if (!filespec->sha1_valid) {
+               if (diff_populate_filespec(filespec, 0))
+                       return 0;
+               hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
+       }
+       memcpy(&hash, filespec->sha1, sizeof(hash));
+       return hash;
+}
+
+static int find_identical_files(struct hashmap *srcs,
+                               int dst_index,
                                struct diff_options *options)
 {
        int renames = 0;
 
+       struct diff_filespec *target = rename_dst[dst_index].two;
+       struct file_similarity *p, *best, dst;
+       int i = 100, best_score = -1;
+
        /*
-        * Walk over all the destinations ...
+        * Find the best source match for specified destination.
         */
-       do {
-               struct diff_filespec *target = dst->filespec;
-               struct file_similarity *p, *best;
-               int i = 100, best_score = -1;
-
-               /*
-                * .. to find the best source match
-                */
-               best = NULL;
-               for (p = src; p; p = p->next) {
-                       int score;
-                       struct diff_filespec *source = p->filespec;
-
-                       /* False hash collision? */
-                       if (hashcmp(source->sha1, target->sha1))
-                               continue;
-                       /* Non-regular files? If so, the modes must match! */
-                       if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
-                               if (source->mode != target->mode)
-                                       continue;
-                       }
-                       /* Give higher scores to sources that haven't been used already */
-                       score = !source->rename_used;
-                       if (source->rename_used && options->detect_rename != DIFF_DETECT_COPY)
+       best = NULL;
+       hashmap_entry_init(&dst, hash_filespec(target));
+       for (p = hashmap_get(srcs, &dst, NULL); p; p = hashmap_get_next(srcs, p)) {
+               int score;
+               struct diff_filespec *source = p->filespec;
+
+               /* False hash collision? */
+               if (hashcmp(source->sha1, target->sha1))
+                       continue;
+               /* Non-regular files? If so, the modes must match! */
+               if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
+                       if (source->mode != target->mode)
                                continue;
-                       score += basename_same(source, target);
-                       if (score > best_score) {
-                               best = p;
-                               best_score = score;
-                               if (score == 2)
-                                       break;
-                       }
-
-                       /* Too many identical alternatives? Pick one */
-                       if (!--i)
-                               break;
                }
-               if (best) {
-                       record_rename_pair(dst->index, best->index, MAX_SCORE);
-                       renames++;
+               /* Give higher scores to sources that haven't been used already */
+               score = !source->rename_used;
+               if (source->rename_used && options->detect_rename != DIFF_DETECT_COPY)
+                       continue;
+               score += basename_same(source, target);
+               if (score > best_score) {
+                       best = p;
+                       best_score = score;
+                       if (score == 2)
+                               break;
                }
-       } while ((dst = dst->next) != NULL);
-       return renames;
-}
 
-static void free_similarity_list(struct file_similarity *p)
-{
-       while (p) {
-               struct file_similarity *entry = p;
-               p = p->next;
-               free(entry);
+               /* Too many identical alternatives? Pick one */
+               if (!--i)
+                       break;
        }
-}
-
-static int find_same_files(void *ptr, void *data)
-{
-       int ret;
-       struct file_similarity *p = ptr;
-       struct file_similarity *src = NULL, *dst = NULL;
-       struct diff_options *options = data;
-
-       /* Split the hash list up into sources and destinations */
-       do {
-               struct file_similarity *entry = p;
-               p = p->next;
-               if (entry->src_dst < 0) {
-                       entry->next = src;
-                       src = entry;
-               } else {
-                       entry->next = dst;
-                       dst = entry;
-               }
-       } while (p);
-
-       /*
-        * If we have both sources *and* destinations, see if
-        * we can match them up
-        */
-       ret = (src && dst) ? find_identical_files(src, dst, options) : 0;
-
-       /* Free the hashes and return the number of renames found */
-       free_similarity_list(src);
-       free_similarity_list(dst);
-       return ret;
-}
-
-static unsigned int hash_filespec(struct diff_filespec *filespec)
-{
-       unsigned int hash;
-       if (!filespec->sha1_valid) {
-               if (diff_populate_filespec(filespec, 0))
-                       return 0;
-               hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
+       if (best) {
+               record_rename_pair(dst_index, best->index, MAX_SCORE);
+               renames++;
        }
-       memcpy(&hash, filespec->sha1, sizeof(hash));
-       return hash;
+       return renames;
 }
 
-static void insert_file_table(struct hash_table *table, int src_dst, int index, struct diff_filespec *filespec)
+static void insert_file_table(struct hashmap *table, int index, struct diff_filespec *filespec)
 {
-       void **pos;
-       unsigned int hash;
        struct file_similarity *entry = xmalloc(sizeof(*entry));
 
-       entry->src_dst = src_dst;
        entry->index = index;
        entry->filespec = filespec;
-       entry->next = NULL;
-
-       hash = hash_filespec(filespec);
-       pos = insert_hash(hash, entry, table);
 
-       /* We already had an entry there? */
-       if (pos) {
-               entry->next = *pos;
-               *pos = entry;
-       }
+       hashmap_entry_init(entry, hash_filespec(filespec));
+       hashmap_add(table, entry);
 }
 
 /*
@@ -385,24 +330,22 @@ static void insert_file_table(struct hash_table *table, int src_dst, int index,
  */
 static int find_exact_renames(struct diff_options *options)
 {
-       int i;
-       struct hash_table file_table;
+       int i, renames = 0;
+       struct hashmap file_table;
 
-       init_hash(&file_table);
-       preallocate_hash(&file_table, rename_src_nr + rename_dst_nr);
+       /* Add all sources to the hash table */
+       hashmap_init(&file_table, NULL, rename_src_nr);
        for (i = 0; i < rename_src_nr; i++)
-               insert_file_table(&file_table, -1, i, rename_src[i].p->one);
+               insert_file_table(&file_table, i, rename_src[i].p->one);
 
+       /* Walk the destinations and find best source match */
        for (i = 0; i < rename_dst_nr; i++)
-               insert_file_table(&file_table, 1, i, rename_dst[i].two);
-
-       /* Find the renames */
-       i = for_each_hash(&file_table, find_same_files, options);
+               renames += find_identical_files(&file_table, i, options);
 
-       /* .. and free the hash data structure */
-       free_hash(&file_table);
+       /* Free the hash data structure and entries */
+       hashmap_free(&file_table, 1);
 
-       return i;
+       return renames;
 }
 
 #define NUM_CANDIDATE_PER_DST 4
index 79de8cf28dc6919f069ae44f7f036a46264452e9..92788ee4de8e59068a204729a8d6f7f89aaa6ee6 100644 (file)
@@ -68,6 +68,8 @@ struct diff_filepair {
        unsigned broken_pair : 1;
        unsigned renamed_pair : 1;
        unsigned is_unmerged : 1;
+       unsigned done_skip_stat_unmatch : 1;
+       unsigned skip_stat_unmatch_result : 1;
 };
 #define DIFF_PAIR_UNMERGED(p) ((p)->is_unmerged)
 
@@ -109,6 +111,20 @@ extern void diffcore_merge_broken(void);
 extern void diffcore_pickaxe(struct diff_options *);
 extern void diffcore_order(const char *orderfile);
 
+/* low-level interface to diffcore_order */
+struct obj_order {
+       void *obj;      /* setup by caller */
+
+       /* setup/used by order_objects() */
+       int orig_order;
+       int order;
+};
+
+typedef const char *(*obj_path_fn_t)(void *obj);
+
+void order_objects(const char *orderfile, obj_path_fn_t obj_path,
+                  struct obj_order *objs, int nr);
+
 #define DIFF_DEBUG 0
 #if DIFF_DEBUG
 void diff_debug_filespec(struct diff_filespec *, int, const char *);
diff --git a/dir.c b/dir.c
index b35b6330f850f610b582b189d7e4d6a9ba4495db..6c6a5d13c2e65b02bfd9e3201c35b3a9f270d093 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -195,6 +195,9 @@ int within_depth(const char *name, int namelen,
        return 1;
 }
 
+#define DO_MATCH_EXCLUDE   1
+#define DO_MATCH_DIRECTORY 2
+
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -208,7 +211,7 @@ int within_depth(const char *name, int namelen,
  * It returns 0 when there is no match.
  */
 static int match_pathspec_item(const struct pathspec_item *item, int prefix,
-                              const char *name, int namelen)
+                              const char *name, int namelen, unsigned flags)
 {
        /* name/namelen has prefix cut off by caller */
        const char *match = item->match + prefix;
@@ -218,7 +221,7 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
         * The normal call pattern is:
         * 1. prefix = common_prefix_len(ps);
         * 2. prune something, or fill_directory
-        * 3. match_pathspec_depth()
+        * 3. match_pathspec()
         *
         * 'prefix' at #1 may be shorter than the command's prefix and
         * it's ok for #2 to match extra files. Those extras will be
@@ -257,7 +260,11 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
 
                if (match[matchlen-1] == '/' || name[matchlen] == '/')
                        return MATCHED_RECURSIVELY;
-       }
+       } else if ((flags & DO_MATCH_DIRECTORY) &&
+                  match[matchlen - 1] == '/' &&
+                  namelen == matchlen - 1 &&
+                  !ps_strncmp(item, match, name, namelen))
+               return MATCHED_EXACTLY;
 
        if (item->nowildcard_len < item->len &&
            !git_fnmatch(item, match, name,
@@ -282,12 +289,12 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
  * pathspec did not match any names, which could indicate that the
  * user mistyped the nth pathspec.
  */
-static int match_pathspec_depth_1(const struct pathspec *ps,
-                                 const char *name, int namelen,
-                                 int prefix, char *seen,
-                                 int exclude)
+static int do_match_pathspec(const struct pathspec *ps,
+                            const char *name, int namelen,
+                            int prefix, char *seen,
+                            unsigned flags)
 {
-       int i, retval = 0;
+       int i, retval = 0, exclude = flags & DO_MATCH_EXCLUDE;
 
        GUARD_PATHSPEC(ps,
                       PATHSPEC_FROMTOP |
@@ -327,7 +334,8 @@ static int match_pathspec_depth_1(const struct pathspec *ps,
                 */
                if (seen && ps->items[i].magic & PATHSPEC_EXCLUDE)
                        seen[i] = MATCHED_FNMATCH;
-               how = match_pathspec_item(ps->items+i, prefix, name, namelen);
+               how = match_pathspec_item(ps->items+i, prefix, name,
+                                         namelen, flags);
                if (ps->recursive &&
                    (ps->magic & PATHSPEC_MAXDEPTH) &&
                    ps->max_depth != -1 &&
@@ -350,15 +358,19 @@ static int match_pathspec_depth_1(const struct pathspec *ps,
        return retval;
 }
 
-int match_pathspec_depth(const struct pathspec *ps,
-                        const char *name, int namelen,
-                        int prefix, char *seen)
+int match_pathspec(const struct pathspec *ps,
+                  const char *name, int namelen,
+                  int prefix, char *seen, int is_dir)
 {
        int positive, negative;
-       positive = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 0);
+       unsigned flags = is_dir ? DO_MATCH_DIRECTORY : 0;
+       positive = do_match_pathspec(ps, name, namelen,
+                                    prefix, seen, flags);
        if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)
                return positive;
-       negative = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 1);
+       negative = do_match_pathspec(ps, name, namelen,
+                                    prefix, seen,
+                                    flags | DO_MATCH_EXCLUDE);
        return negative ? 0 : positive;
 }
 
@@ -491,6 +503,25 @@ void clear_exclude_list(struct exclude_list *el)
        el->filebuf = NULL;
 }
 
+static void trim_trailing_spaces(char *buf)
+{
+       int i, last_space = -1, nr_spaces, len = strlen(buf);
+       for (i = 0; i < len; i++)
+               if (buf[i] == '\\')
+                       i++;
+               else if (buf[i] == ' ') {
+                       if (last_space == -1) {
+                               last_space = i;
+                               nr_spaces = 1;
+                       } else
+                               nr_spaces++;
+               } else
+                       last_space = -1;
+
+       if (last_space != -1 && last_space + nr_spaces == len)
+               buf[last_space] = '\0';
+}
+
 int add_excludes_from_file_to_list(const char *fname,
                                   const char *base,
                                   int baselen,
@@ -542,6 +573,7 @@ int add_excludes_from_file_to_list(const char *fname,
                if (buf[i] == '\n') {
                        if (entry != buf + i && entry[0] != '#') {
                                buf[i - (i && buf[i-1] == '\r')] = 0;
+                               trim_trailing_spaces(entry);
                                add_exclude(entry, base, baselen, el, lineno);
                        }
                        lineno++;
diff --git a/dir.h b/dir.h
index 9b7e4e77d8b11bab92a91a6ce3e8920e50d23f9b..55e53456afab4c9fb8441144ac7393458b553006 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -132,9 +132,9 @@ struct dir_struct {
 extern int simple_length(const char *match);
 extern int no_wildcard(const char *string);
 extern char *common_prefix(const struct pathspec *pathspec);
-extern int match_pathspec_depth(const struct pathspec *pathspec,
-                               const char *name, int namelen,
-                               int prefix, char *seen);
+extern int match_pathspec(const struct pathspec *pathspec,
+                         const char *name, int namelen,
+                         int prefix, char *seen, int is_dir);
 extern int within_depth(const char *name, int namelen, int depth, int max_depth);
 
 extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec);
@@ -205,4 +205,22 @@ extern int git_fnmatch(const struct pathspec_item *item,
                       const char *pattern, const char *string,
                       int prefix);
 
+static inline int ce_path_match(const struct cache_entry *ce,
+                               const struct pathspec *pathspec,
+                               char *seen)
+{
+       return match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen,
+                             S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
+}
+
+static inline int dir_path_match(const struct dir_entry *ent,
+                                const struct pathspec *pathspec,
+                                int prefix, char *seen)
+{
+       int has_trailing_dir = ent->len && ent->name[ent->len - 1] == '/';
+       int len = has_trailing_dir ? ent->len - 1 : ent->len;
+       return match_pathspec(pathspec, ent->name, len, prefix, seen,
+                             has_trailing_dir);
+}
+
 #endif
diff --git a/ewah/bitmap.c b/ewah/bitmap.c
new file mode 100644 (file)
index 0000000..710e58c
--- /dev/null
@@ -0,0 +1,221 @@
+/**
+ * Copyright 2013, GitHub, Inc
+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,
+ *     David McIntosh, Robert Becho, Google Inc. and Veronika Zenz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "git-compat-util.h"
+#include "ewok.h"
+
+#define MASK(x) ((eword_t)1 << (x % BITS_IN_WORD))
+#define BLOCK(x) (x / BITS_IN_WORD)
+
+struct bitmap *bitmap_new(void)
+{
+       struct bitmap *bitmap = ewah_malloc(sizeof(struct bitmap));
+       bitmap->words = ewah_calloc(32, sizeof(eword_t));
+       bitmap->word_alloc = 32;
+       return bitmap;
+}
+
+void bitmap_set(struct bitmap *self, size_t pos)
+{
+       size_t block = BLOCK(pos);
+
+       if (block >= self->word_alloc) {
+               size_t old_size = self->word_alloc;
+               self->word_alloc = block * 2;
+               self->words = ewah_realloc(self->words,
+                       self->word_alloc * sizeof(eword_t));
+
+               memset(self->words + old_size, 0x0,
+                       (self->word_alloc - old_size) * sizeof(eword_t));
+       }
+
+       self->words[block] |= MASK(pos);
+}
+
+void bitmap_clear(struct bitmap *self, size_t pos)
+{
+       size_t block = BLOCK(pos);
+
+       if (block < self->word_alloc)
+               self->words[block] &= ~MASK(pos);
+}
+
+int bitmap_get(struct bitmap *self, size_t pos)
+{
+       size_t block = BLOCK(pos);
+       return block < self->word_alloc &&
+               (self->words[block] & MASK(pos)) != 0;
+}
+
+struct ewah_bitmap *bitmap_to_ewah(struct bitmap *bitmap)
+{
+       struct ewah_bitmap *ewah = ewah_new();
+       size_t i, running_empty_words = 0;
+       eword_t last_word = 0;
+
+       for (i = 0; i < bitmap->word_alloc; ++i) {
+               if (bitmap->words[i] == 0) {
+                       running_empty_words++;
+                       continue;
+               }
+
+               if (last_word != 0)
+                       ewah_add(ewah, last_word);
+
+               if (running_empty_words > 0) {
+                       ewah_add_empty_words(ewah, 0, running_empty_words);
+                       running_empty_words = 0;
+               }
+
+               last_word = bitmap->words[i];
+       }
+
+       ewah_add(ewah, last_word);
+       return ewah;
+}
+
+struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah)
+{
+       struct bitmap *bitmap = bitmap_new();
+       struct ewah_iterator it;
+       eword_t blowup;
+       size_t i = 0;
+
+       ewah_iterator_init(&it, ewah);
+
+       while (ewah_iterator_next(&blowup, &it)) {
+               if (i >= bitmap->word_alloc) {
+                       bitmap->word_alloc *= 1.5;
+                       bitmap->words = ewah_realloc(
+                               bitmap->words, bitmap->word_alloc * sizeof(eword_t));
+               }
+
+               bitmap->words[i++] = blowup;
+       }
+
+       bitmap->word_alloc = i;
+       return bitmap;
+}
+
+void bitmap_and_not(struct bitmap *self, struct bitmap *other)
+{
+       const size_t count = (self->word_alloc < other->word_alloc) ?
+               self->word_alloc : other->word_alloc;
+
+       size_t i;
+
+       for (i = 0; i < count; ++i)
+               self->words[i] &= ~other->words[i];
+}
+
+void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)
+{
+       size_t original_size = self->word_alloc;
+       size_t other_final = (other->bit_size / BITS_IN_WORD) + 1;
+       size_t i = 0;
+       struct ewah_iterator it;
+       eword_t word;
+
+       if (self->word_alloc < other_final) {
+               self->word_alloc = other_final;
+               self->words = ewah_realloc(self->words,
+                       self->word_alloc * sizeof(eword_t));
+               memset(self->words + original_size, 0x0,
+                       (self->word_alloc - original_size) * sizeof(eword_t));
+       }
+
+       ewah_iterator_init(&it, other);
+
+       while (ewah_iterator_next(&word, &it))
+               self->words[i++] |= word;
+}
+
+void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data)
+{
+       size_t pos = 0, i;
+
+       for (i = 0; i < self->word_alloc; ++i) {
+               eword_t word = self->words[i];
+               uint32_t offset;
+
+               if (word == (eword_t)~0) {
+                       for (offset = 0; offset < BITS_IN_WORD; ++offset)
+                               callback(pos++, data);
+               } else {
+                       for (offset = 0; offset < BITS_IN_WORD; ++offset) {
+                               if ((word >> offset) == 0)
+                                       break;
+
+                               offset += ewah_bit_ctz64(word >> offset);
+                               callback(pos + offset, data);
+                       }
+                       pos += BITS_IN_WORD;
+               }
+       }
+}
+
+size_t bitmap_popcount(struct bitmap *self)
+{
+       size_t i, count = 0;
+
+       for (i = 0; i < self->word_alloc; ++i)
+               count += ewah_bit_popcount64(self->words[i]);
+
+       return count;
+}
+
+int bitmap_equals(struct bitmap *self, struct bitmap *other)
+{
+       struct bitmap *big, *small;
+       size_t i;
+
+       if (self->word_alloc < other->word_alloc) {
+               small = self;
+               big = other;
+       } else {
+               small = other;
+               big = self;
+       }
+
+       for (i = 0; i < small->word_alloc; ++i) {
+               if (small->words[i] != big->words[i])
+                       return 0;
+       }
+
+       for (; i < big->word_alloc; ++i) {
+               if (big->words[i] != 0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+void bitmap_reset(struct bitmap *bitmap)
+{
+       memset(bitmap->words, 0x0, bitmap->word_alloc * sizeof(eword_t));
+}
+
+void bitmap_free(struct bitmap *bitmap)
+{
+       if (bitmap == NULL)
+               return;
+
+       free(bitmap->words);
+       free(bitmap);
+}
diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c
new file mode 100644 (file)
index 0000000..9ced2da
--- /dev/null
@@ -0,0 +1,714 @@
+/**
+ * Copyright 2013, GitHub, Inc
+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,
+ *     David McIntosh, Robert Becho, Google Inc. and Veronika Zenz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "git-compat-util.h"
+#include "ewok.h"
+#include "ewok_rlw.h"
+
+static inline size_t min_size(size_t a, size_t b)
+{
+       return a < b ? a : b;
+}
+
+static inline size_t max_size(size_t a, size_t b)
+{
+       return a > b ? a : b;
+}
+
+static inline void buffer_grow(struct ewah_bitmap *self, size_t new_size)
+{
+       size_t rlw_offset = (uint8_t *)self->rlw - (uint8_t *)self->buffer;
+
+       if (self->alloc_size >= new_size)
+               return;
+
+       self->alloc_size = new_size;
+       self->buffer = ewah_realloc(self->buffer,
+               self->alloc_size * sizeof(eword_t));
+       self->rlw = self->buffer + (rlw_offset / sizeof(size_t));
+}
+
+static inline void buffer_push(struct ewah_bitmap *self, eword_t value)
+{
+       if (self->buffer_size + 1 >= self->alloc_size)
+               buffer_grow(self, self->buffer_size * 3 / 2);
+
+       self->buffer[self->buffer_size++] = value;
+}
+
+static void buffer_push_rlw(struct ewah_bitmap *self, eword_t value)
+{
+       buffer_push(self, value);
+       self->rlw = self->buffer + self->buffer_size - 1;
+}
+
+static size_t add_empty_words(struct ewah_bitmap *self, int v, size_t number)
+{
+       size_t added = 0;
+       eword_t runlen, can_add;
+
+       if (rlw_get_run_bit(self->rlw) != v && rlw_size(self->rlw) == 0) {
+               rlw_set_run_bit(self->rlw, v);
+       } else if (rlw_get_literal_words(self->rlw) != 0 ||
+                       rlw_get_run_bit(self->rlw) != v) {
+               buffer_push_rlw(self, 0);
+               if (v) rlw_set_run_bit(self->rlw, v);
+               added++;
+       }
+
+       runlen = rlw_get_running_len(self->rlw);
+       can_add = min_size(number, RLW_LARGEST_RUNNING_COUNT - runlen);
+
+       rlw_set_running_len(self->rlw, runlen + can_add);
+       number -= can_add;
+
+       while (number >= RLW_LARGEST_RUNNING_COUNT) {
+               buffer_push_rlw(self, 0);
+               added++;
+               if (v) rlw_set_run_bit(self->rlw, v);
+               rlw_set_running_len(self->rlw, RLW_LARGEST_RUNNING_COUNT);
+               number -= RLW_LARGEST_RUNNING_COUNT;
+       }
+
+       if (number > 0) {
+               buffer_push_rlw(self, 0);
+               added++;
+
+               if (v) rlw_set_run_bit(self->rlw, v);
+               rlw_set_running_len(self->rlw, number);
+       }
+
+       return added;
+}
+
+size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number)
+{
+       if (number == 0)
+               return 0;
+
+       self->bit_size += number * BITS_IN_WORD;
+       return add_empty_words(self, v, number);
+}
+
+static size_t add_literal(struct ewah_bitmap *self, eword_t new_data)
+{
+       eword_t current_num = rlw_get_literal_words(self->rlw);
+
+       if (current_num >= RLW_LARGEST_LITERAL_COUNT) {
+               buffer_push_rlw(self, 0);
+
+               rlw_set_literal_words(self->rlw, 1);
+               buffer_push(self, new_data);
+               return 2;
+       }
+
+       rlw_set_literal_words(self->rlw, current_num + 1);
+
+       /* sanity check */
+       assert(rlw_get_literal_words(self->rlw) == current_num + 1);
+
+       buffer_push(self, new_data);
+       return 1;
+}
+
+void ewah_add_dirty_words(
+       struct ewah_bitmap *self, const eword_t *buffer,
+       size_t number, int negate)
+{
+       size_t literals, can_add;
+
+       while (1) {
+               literals = rlw_get_literal_words(self->rlw);
+               can_add = min_size(number, RLW_LARGEST_LITERAL_COUNT - literals);
+
+               rlw_set_literal_words(self->rlw, literals + can_add);
+
+               if (self->buffer_size + can_add >= self->alloc_size)
+                       buffer_grow(self, (self->buffer_size + can_add) * 3 / 2);
+
+               if (negate) {
+                       size_t i;
+                       for (i = 0; i < can_add; ++i)
+                               self->buffer[self->buffer_size++] = ~buffer[i];
+               } else {
+                       memcpy(self->buffer + self->buffer_size,
+                               buffer, can_add * sizeof(eword_t));
+                       self->buffer_size += can_add;
+               }
+
+               self->bit_size += can_add * BITS_IN_WORD;
+
+               if (number - can_add == 0)
+                       break;
+
+               buffer_push_rlw(self, 0);
+               buffer += can_add;
+               number -= can_add;
+       }
+}
+
+static size_t add_empty_word(struct ewah_bitmap *self, int v)
+{
+       int no_literal = (rlw_get_literal_words(self->rlw) == 0);
+       eword_t run_len = rlw_get_running_len(self->rlw);
+
+       if (no_literal && run_len == 0) {
+               rlw_set_run_bit(self->rlw, v);
+               assert(rlw_get_run_bit(self->rlw) == v);
+       }
+
+       if (no_literal && rlw_get_run_bit(self->rlw) == v &&
+               run_len < RLW_LARGEST_RUNNING_COUNT) {
+               rlw_set_running_len(self->rlw, run_len + 1);
+               assert(rlw_get_running_len(self->rlw) == run_len + 1);
+               return 0;
+       } else {
+               buffer_push_rlw(self, 0);
+
+               assert(rlw_get_running_len(self->rlw) == 0);
+               assert(rlw_get_run_bit(self->rlw) == 0);
+               assert(rlw_get_literal_words(self->rlw) == 0);
+
+               rlw_set_run_bit(self->rlw, v);
+               assert(rlw_get_run_bit(self->rlw) == v);
+
+               rlw_set_running_len(self->rlw, 1);
+               assert(rlw_get_running_len(self->rlw) == 1);
+               assert(rlw_get_literal_words(self->rlw) == 0);
+               return 1;
+       }
+}
+
+size_t ewah_add(struct ewah_bitmap *self, eword_t word)
+{
+       self->bit_size += BITS_IN_WORD;
+
+       if (word == 0)
+               return add_empty_word(self, 0);
+
+       if (word == (eword_t)(~0))
+               return add_empty_word(self, 1);
+
+       return add_literal(self, word);
+}
+
+void ewah_set(struct ewah_bitmap *self, size_t i)
+{
+       const size_t dist =
+               (i + BITS_IN_WORD) / BITS_IN_WORD -
+               (self->bit_size + BITS_IN_WORD - 1) / BITS_IN_WORD;
+
+       assert(i >= self->bit_size);
+
+       self->bit_size = i + 1;
+
+       if (dist > 0) {
+               if (dist > 1)
+                       add_empty_words(self, 0, dist - 1);
+
+               add_literal(self, (eword_t)1 << (i % BITS_IN_WORD));
+               return;
+       }
+
+       if (rlw_get_literal_words(self->rlw) == 0) {
+               rlw_set_running_len(self->rlw,
+                       rlw_get_running_len(self->rlw) - 1);
+               add_literal(self, (eword_t)1 << (i % BITS_IN_WORD));
+               return;
+       }
+
+       self->buffer[self->buffer_size - 1] |=
+               ((eword_t)1 << (i % BITS_IN_WORD));
+
+       /* check if we just completed a stream of 1s */
+       if (self->buffer[self->buffer_size - 1] == (eword_t)(~0)) {
+               self->buffer[--self->buffer_size] = 0;
+               rlw_set_literal_words(self->rlw,
+                       rlw_get_literal_words(self->rlw) - 1);
+               add_empty_word(self, 1);
+       }
+}
+
+void ewah_each_bit(struct ewah_bitmap *self, void (*callback)(size_t, void*), void *payload)
+{
+       size_t pos = 0;
+       size_t pointer = 0;
+       size_t k;
+
+       while (pointer < self->buffer_size) {
+               eword_t *word = &self->buffer[pointer];
+
+               if (rlw_get_run_bit(word)) {
+                       size_t len = rlw_get_running_len(word) * BITS_IN_WORD;
+                       for (k = 0; k < len; ++k, ++pos)
+                               callback(pos, payload);
+               } else {
+                       pos += rlw_get_running_len(word) * BITS_IN_WORD;
+               }
+
+               ++pointer;
+
+               for (k = 0; k < rlw_get_literal_words(word); ++k) {
+                       int c;
+
+                       /* todo: zero count optimization */
+                       for (c = 0; c < BITS_IN_WORD; ++c, ++pos) {
+                               if ((self->buffer[pointer] & ((eword_t)1 << c)) != 0)
+                                       callback(pos, payload);
+                       }
+
+                       ++pointer;
+               }
+       }
+}
+
+struct ewah_bitmap *ewah_new(void)
+{
+       struct ewah_bitmap *self;
+
+       self = ewah_malloc(sizeof(struct ewah_bitmap));
+       if (self == NULL)
+               return NULL;
+
+       self->buffer = ewah_malloc(32 * sizeof(eword_t));
+       self->alloc_size = 32;
+
+       ewah_clear(self);
+       return self;
+}
+
+void ewah_clear(struct ewah_bitmap *self)
+{
+       self->buffer_size = 1;
+       self->buffer[0] = 0;
+       self->bit_size = 0;
+       self->rlw = self->buffer;
+}
+
+void ewah_free(struct ewah_bitmap *self)
+{
+       if (!self)
+               return;
+
+       if (self->alloc_size)
+               free(self->buffer);
+
+       free(self);
+}
+
+static void read_new_rlw(struct ewah_iterator *it)
+{
+       const eword_t *word = NULL;
+
+       it->literals = 0;
+       it->compressed = 0;
+
+       while (1) {
+               word = &it->buffer[it->pointer];
+
+               it->rl = rlw_get_running_len(word);
+               it->lw = rlw_get_literal_words(word);
+               it->b = rlw_get_run_bit(word);
+
+               if (it->rl || it->lw)
+                       return;
+
+               if (it->pointer < it->buffer_size - 1) {
+                       it->pointer++;
+               } else {
+                       it->pointer = it->buffer_size;
+                       return;
+               }
+       }
+}
+
+int ewah_iterator_next(eword_t *next, struct ewah_iterator *it)
+{
+       if (it->pointer >= it->buffer_size)
+               return 0;
+
+       if (it->compressed < it->rl) {
+               it->compressed++;
+               *next = it->b ? (eword_t)(~0) : 0;
+       } else {
+               assert(it->literals < it->lw);
+
+               it->literals++;
+               it->pointer++;
+
+               assert(it->pointer < it->buffer_size);
+
+               *next = it->buffer[it->pointer];
+       }
+
+       if (it->compressed == it->rl && it->literals == it->lw) {
+               if (++it->pointer < it->buffer_size)
+                       read_new_rlw(it);
+       }
+
+       return 1;
+}
+
+void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent)
+{
+       it->buffer = parent->buffer;
+       it->buffer_size = parent->buffer_size;
+       it->pointer = 0;
+
+       it->lw = 0;
+       it->rl = 0;
+       it->compressed = 0;
+       it->literals = 0;
+       it->b = 0;
+
+       if (it->pointer < it->buffer_size)
+               read_new_rlw(it);
+}
+
+void ewah_not(struct ewah_bitmap *self)
+{
+       size_t pointer = 0;
+
+       while (pointer < self->buffer_size) {
+               eword_t *word = &self->buffer[pointer];
+               size_t literals, k;
+
+               rlw_xor_run_bit(word);
+               ++pointer;
+
+               literals = rlw_get_literal_words(word);
+               for (k = 0; k < literals; ++k) {
+                       self->buffer[pointer] = ~self->buffer[pointer];
+                       ++pointer;
+               }
+       }
+}
+
+void ewah_xor(
+       struct ewah_bitmap *ewah_i,
+       struct ewah_bitmap *ewah_j,
+       struct ewah_bitmap *out)
+{
+       struct rlw_iterator rlw_i;
+       struct rlw_iterator rlw_j;
+       size_t literals;
+
+       rlwit_init(&rlw_i, ewah_i);
+       rlwit_init(&rlw_j, ewah_j);
+
+       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
+               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
+                       struct rlw_iterator *prey, *predator;
+                       size_t index;
+                       int negate_words;
+
+                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
+                               prey = &rlw_i;
+                               predator = &rlw_j;
+                       } else {
+                               prey = &rlw_j;
+                               predator = &rlw_i;
+                       }
+
+                       negate_words = !!predator->rlw.running_bit;
+                       index = rlwit_discharge(prey, out,
+                               predator->rlw.running_len, negate_words);
+
+                       ewah_add_empty_words(out, negate_words,
+                               predator->rlw.running_len - index);
+
+                       rlwit_discard_first_words(predator,
+                               predator->rlw.running_len);
+               }
+
+               literals = min_size(
+                       rlw_i.rlw.literal_words,
+                       rlw_j.rlw.literal_words);
+
+               if (literals) {
+                       size_t k;
+
+                       for (k = 0; k < literals; ++k) {
+                               ewah_add(out,
+                                       rlw_i.buffer[rlw_i.literal_word_start + k] ^
+                                       rlw_j.buffer[rlw_j.literal_word_start + k]
+                               );
+                       }
+
+                       rlwit_discard_first_words(&rlw_i, literals);
+                       rlwit_discard_first_words(&rlw_j, literals);
+               }
+       }
+
+       if (rlwit_word_size(&rlw_i) > 0)
+               rlwit_discharge(&rlw_i, out, ~0, 0);
+       else
+               rlwit_discharge(&rlw_j, out, ~0, 0);
+
+       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
+}
+
+void ewah_and(
+       struct ewah_bitmap *ewah_i,
+       struct ewah_bitmap *ewah_j,
+       struct ewah_bitmap *out)
+{
+       struct rlw_iterator rlw_i;
+       struct rlw_iterator rlw_j;
+       size_t literals;
+
+       rlwit_init(&rlw_i, ewah_i);
+       rlwit_init(&rlw_j, ewah_j);
+
+       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
+               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
+                       struct rlw_iterator *prey, *predator;
+
+                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
+                               prey = &rlw_i;
+                               predator = &rlw_j;
+                       } else {
+                               prey = &rlw_j;
+                               predator = &rlw_i;
+                       }
+
+                       if (predator->rlw.running_bit == 0) {
+                               ewah_add_empty_words(out, 0,
+                                       predator->rlw.running_len);
+                               rlwit_discard_first_words(prey,
+                                       predator->rlw.running_len);
+                               rlwit_discard_first_words(predator,
+                                       predator->rlw.running_len);
+                       } else {
+                               size_t index = rlwit_discharge(prey, out,
+                                       predator->rlw.running_len, 0);
+                               ewah_add_empty_words(out, 0,
+                                       predator->rlw.running_len - index);
+                               rlwit_discard_first_words(predator,
+                                       predator->rlw.running_len);
+                       }
+               }
+
+               literals = min_size(
+                       rlw_i.rlw.literal_words,
+                       rlw_j.rlw.literal_words);
+
+               if (literals) {
+                       size_t k;
+
+                       for (k = 0; k < literals; ++k) {
+                               ewah_add(out,
+                                       rlw_i.buffer[rlw_i.literal_word_start + k] &
+                                       rlw_j.buffer[rlw_j.literal_word_start + k]
+                               );
+                       }
+
+                       rlwit_discard_first_words(&rlw_i, literals);
+                       rlwit_discard_first_words(&rlw_j, literals);
+               }
+       }
+
+       if (rlwit_word_size(&rlw_i) > 0)
+               rlwit_discharge_empty(&rlw_i, out);
+       else
+               rlwit_discharge_empty(&rlw_j, out);
+
+       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
+}
+
+void ewah_and_not(
+       struct ewah_bitmap *ewah_i,
+       struct ewah_bitmap *ewah_j,
+       struct ewah_bitmap *out)
+{
+       struct rlw_iterator rlw_i;
+       struct rlw_iterator rlw_j;
+       size_t literals;
+
+       rlwit_init(&rlw_i, ewah_i);
+       rlwit_init(&rlw_j, ewah_j);
+
+       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
+               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
+                       struct rlw_iterator *prey, *predator;
+
+                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
+                               prey = &rlw_i;
+                               predator = &rlw_j;
+                       } else {
+                               prey = &rlw_j;
+                               predator = &rlw_i;
+                       }
+
+                       if ((predator->rlw.running_bit && prey == &rlw_i) ||
+                               (!predator->rlw.running_bit && prey != &rlw_i)) {
+                               ewah_add_empty_words(out, 0,
+                                       predator->rlw.running_len);
+                               rlwit_discard_first_words(prey,
+                                       predator->rlw.running_len);
+                               rlwit_discard_first_words(predator,
+                                       predator->rlw.running_len);
+                       } else {
+                               size_t index;
+                               int negate_words;
+
+                               negate_words = (&rlw_i != prey);
+                               index = rlwit_discharge(prey, out,
+                                       predator->rlw.running_len, negate_words);
+                               ewah_add_empty_words(out, negate_words,
+                                       predator->rlw.running_len - index);
+                               rlwit_discard_first_words(predator,
+                                       predator->rlw.running_len);
+                       }
+               }
+
+               literals = min_size(
+                       rlw_i.rlw.literal_words,
+                       rlw_j.rlw.literal_words);
+
+               if (literals) {
+                       size_t k;
+
+                       for (k = 0; k < literals; ++k) {
+                               ewah_add(out,
+                                       rlw_i.buffer[rlw_i.literal_word_start + k] &
+                                       ~(rlw_j.buffer[rlw_j.literal_word_start + k])
+                               );
+                       }
+
+                       rlwit_discard_first_words(&rlw_i, literals);
+                       rlwit_discard_first_words(&rlw_j, literals);
+               }
+       }
+
+       if (rlwit_word_size(&rlw_i) > 0)
+               rlwit_discharge(&rlw_i, out, ~0, 0);
+       else
+               rlwit_discharge_empty(&rlw_j, out);
+
+       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
+}
+
+void ewah_or(
+       struct ewah_bitmap *ewah_i,
+       struct ewah_bitmap *ewah_j,
+       struct ewah_bitmap *out)
+{
+       struct rlw_iterator rlw_i;
+       struct rlw_iterator rlw_j;
+       size_t literals;
+
+       rlwit_init(&rlw_i, ewah_i);
+       rlwit_init(&rlw_j, ewah_j);
+
+       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
+               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
+                       struct rlw_iterator *prey, *predator;
+
+                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
+                               prey = &rlw_i;
+                               predator = &rlw_j;
+                       } else {
+                               prey = &rlw_j;
+                               predator = &rlw_i;
+                       }
+
+                       if (predator->rlw.running_bit) {
+                               ewah_add_empty_words(out, 0,
+                                       predator->rlw.running_len);
+                               rlwit_discard_first_words(prey,
+                                       predator->rlw.running_len);
+                               rlwit_discard_first_words(predator,
+                                       predator->rlw.running_len);
+                       } else {
+                               size_t index = rlwit_discharge(prey, out,
+                                       predator->rlw.running_len, 0);
+                               ewah_add_empty_words(out, 0,
+                                       predator->rlw.running_len - index);
+                               rlwit_discard_first_words(predator,
+                                       predator->rlw.running_len);
+                       }
+               }
+
+               literals = min_size(
+                       rlw_i.rlw.literal_words,
+                       rlw_j.rlw.literal_words);
+
+               if (literals) {
+                       size_t k;
+
+                       for (k = 0; k < literals; ++k) {
+                               ewah_add(out,
+                                       rlw_i.buffer[rlw_i.literal_word_start + k] |
+                                       rlw_j.buffer[rlw_j.literal_word_start + k]
+                               );
+                       }
+
+                       rlwit_discard_first_words(&rlw_i, literals);
+                       rlwit_discard_first_words(&rlw_j, literals);
+               }
+       }
+
+       if (rlwit_word_size(&rlw_i) > 0)
+               rlwit_discharge(&rlw_i, out, ~0, 0);
+       else
+               rlwit_discharge(&rlw_j, out, ~0, 0);
+
+       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
+}
+
+
+#define BITMAP_POOL_MAX 16
+static struct ewah_bitmap *bitmap_pool[BITMAP_POOL_MAX];
+static size_t bitmap_pool_size;
+
+struct ewah_bitmap *ewah_pool_new(void)
+{
+       if (bitmap_pool_size)
+               return bitmap_pool[--bitmap_pool_size];
+
+       return ewah_new();
+}
+
+void ewah_pool_free(struct ewah_bitmap *self)
+{
+       if (self == NULL)
+               return;
+
+       if (bitmap_pool_size == BITMAP_POOL_MAX ||
+               self->alloc_size == 0) {
+               ewah_free(self);
+               return;
+       }
+
+       ewah_clear(self);
+       bitmap_pool[bitmap_pool_size++] = self;
+}
+
+uint32_t ewah_checksum(struct ewah_bitmap *self)
+{
+       const uint8_t *p = (uint8_t *)self->buffer;
+       uint32_t crc = (uint32_t)self->bit_size;
+       size_t size = self->buffer_size * sizeof(eword_t);
+
+       while (size--)
+               crc = (crc << 5) - crc + (uint32_t)*p++;
+
+       return crc;
+}
diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c
new file mode 100644 (file)
index 0000000..f7f700e
--- /dev/null
@@ -0,0 +1,204 @@
+/**
+ * Copyright 2013, GitHub, Inc
+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,
+ *     David McIntosh, Robert Becho, Google Inc. and Veronika Zenz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "git-compat-util.h"
+#include "ewok.h"
+
+int ewah_serialize_native(struct ewah_bitmap *self, int fd)
+{
+       uint32_t write32;
+       size_t to_write = self->buffer_size * 8;
+
+       /* 32 bit -- bit size for the map */
+       write32 = (uint32_t)self->bit_size;
+       if (write(fd, &write32, 4) != 4)
+               return -1;
+
+       /** 32 bit -- number of compressed 64-bit words */
+       write32 = (uint32_t)self->buffer_size;
+       if (write(fd, &write32, 4) != 4)
+               return -1;
+
+       if (write(fd, self->buffer, to_write) != to_write)
+               return -1;
+
+       /** 32 bit -- position for the RLW */
+       write32 = self->rlw - self->buffer;
+       if (write(fd, &write32, 4) != 4)
+               return -1;
+
+       return (3 * 4) + to_write;
+}
+
+int ewah_serialize_to(struct ewah_bitmap *self,
+                     int (*write_fun)(void *, const void *, size_t),
+                     void *data)
+{
+       size_t i;
+       eword_t dump[2048];
+       const size_t words_per_dump = sizeof(dump) / sizeof(eword_t);
+       uint32_t bitsize, word_count, rlw_pos;
+
+       const eword_t *buffer;
+       size_t words_left;
+
+       /* 32 bit -- bit size for the map */
+       bitsize =  htonl((uint32_t)self->bit_size);
+       if (write_fun(data, &bitsize, 4) != 4)
+               return -1;
+
+       /** 32 bit -- number of compressed 64-bit words */
+       word_count =  htonl((uint32_t)self->buffer_size);
+       if (write_fun(data, &word_count, 4) != 4)
+               return -1;
+
+       /** 64 bit x N -- compressed words */
+       buffer = self->buffer;
+       words_left = self->buffer_size;
+
+       while (words_left >= words_per_dump) {
+               for (i = 0; i < words_per_dump; ++i, ++buffer)
+                       dump[i] = htonll(*buffer);
+
+               if (write_fun(data, dump, sizeof(dump)) != sizeof(dump))
+                       return -1;
+
+               words_left -= words_per_dump;
+       }
+
+       if (words_left) {
+               for (i = 0; i < words_left; ++i, ++buffer)
+                       dump[i] = htonll(*buffer);
+
+               if (write_fun(data, dump, words_left * 8) != words_left * 8)
+                       return -1;
+       }
+
+       /** 32 bit -- position for the RLW */
+       rlw_pos = (uint8_t*)self->rlw - (uint8_t *)self->buffer;
+       rlw_pos = htonl(rlw_pos / sizeof(eword_t));
+
+       if (write_fun(data, &rlw_pos, 4) != 4)
+               return -1;
+
+       return (3 * 4) + (self->buffer_size * 8);
+}
+
+static int write_helper(void *fd, const void *buf, size_t len)
+{
+       return write((intptr_t)fd, buf, len);
+}
+
+int ewah_serialize(struct ewah_bitmap *self, int fd)
+{
+       return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
+}
+
+int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)
+{
+       uint8_t *ptr = map;
+       size_t i;
+
+       self->bit_size = get_be32(ptr);
+       ptr += sizeof(uint32_t);
+
+       self->buffer_size = self->alloc_size = get_be32(ptr);
+       ptr += sizeof(uint32_t);
+
+       self->buffer = ewah_realloc(self->buffer,
+               self->alloc_size * sizeof(eword_t));
+
+       if (!self->buffer)
+               return -1;
+
+       /*
+        * Copy the raw data for the bitmap as a whole chunk;
+        * if we're in a little-endian platform, we'll perform
+        * the endianness conversion in a separate pass to ensure
+        * we're loading 8-byte aligned words.
+        */
+       memcpy(self->buffer, ptr, self->buffer_size * sizeof(uint64_t));
+       ptr += self->buffer_size * sizeof(uint64_t);
+
+       for (i = 0; i < self->buffer_size; ++i)
+               self->buffer[i] = ntohll(self->buffer[i]);
+
+       self->rlw = self->buffer + get_be32(ptr);
+
+       return (3 * 4) + (self->buffer_size * 8);
+}
+
+int ewah_deserialize(struct ewah_bitmap *self, int fd)
+{
+       size_t i;
+       eword_t dump[2048];
+       const size_t words_per_dump = sizeof(dump) / sizeof(eword_t);
+       uint32_t bitsize, word_count, rlw_pos;
+
+       eword_t *buffer = NULL;
+       size_t words_left;
+
+       ewah_clear(self);
+
+       /* 32 bit -- bit size for the map */
+       if (read(fd, &bitsize, 4) != 4)
+               return -1;
+
+       self->bit_size = (size_t)ntohl(bitsize);
+
+       /** 32 bit -- number of compressed 64-bit words */
+       if (read(fd, &word_count, 4) != 4)
+               return -1;
+
+       self->buffer_size = self->alloc_size = (size_t)ntohl(word_count);
+       self->buffer = ewah_realloc(self->buffer,
+               self->alloc_size * sizeof(eword_t));
+
+       if (!self->buffer)
+               return -1;
+
+       /** 64 bit x N -- compressed words */
+       buffer = self->buffer;
+       words_left = self->buffer_size;
+
+       while (words_left >= words_per_dump) {
+               if (read(fd, dump, sizeof(dump)) != sizeof(dump))
+                       return -1;
+
+               for (i = 0; i < words_per_dump; ++i, ++buffer)
+                       *buffer = ntohll(dump[i]);
+
+               words_left -= words_per_dump;
+       }
+
+       if (words_left) {
+               if (read(fd, dump, words_left * 8) != words_left * 8)
+                       return -1;
+
+               for (i = 0; i < words_left; ++i, ++buffer)
+                       *buffer = ntohll(dump[i]);
+       }
+
+       /** 32 bit -- position for the RLW */
+       if (read(fd, &rlw_pos, 4) != 4)
+               return -1;
+
+       self->rlw = self->buffer + ntohl(rlw_pos);
+       return 0;
+}
diff --git a/ewah/ewah_rlw.c b/ewah/ewah_rlw.c
new file mode 100644 (file)
index 0000000..c723f1a
--- /dev/null
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2013, GitHub, Inc
+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,
+ *     David McIntosh, Robert Becho, Google Inc. and Veronika Zenz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "git-compat-util.h"
+#include "ewok.h"
+#include "ewok_rlw.h"
+
+static inline int next_word(struct rlw_iterator *it)
+{
+       if (it->pointer >= it->size)
+               return 0;
+
+       it->rlw.word = &it->buffer[it->pointer];
+       it->pointer += rlw_get_literal_words(it->rlw.word) + 1;
+
+       it->rlw.literal_words = rlw_get_literal_words(it->rlw.word);
+       it->rlw.running_len = rlw_get_running_len(it->rlw.word);
+       it->rlw.running_bit = rlw_get_run_bit(it->rlw.word);
+       it->rlw.literal_word_offset = 0;
+
+       return 1;
+}
+
+void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *from_ewah)
+{
+       it->buffer = from_ewah->buffer;
+       it->size = from_ewah->buffer_size;
+       it->pointer = 0;
+
+       next_word(it);
+
+       it->literal_word_start = rlwit_literal_words(it) +
+               it->rlw.literal_word_offset;
+}
+
+void rlwit_discard_first_words(struct rlw_iterator *it, size_t x)
+{
+       while (x > 0) {
+               size_t discard;
+
+               if (it->rlw.running_len > x) {
+                       it->rlw.running_len -= x;
+                       return;
+               }
+
+               x -= it->rlw.running_len;
+               it->rlw.running_len = 0;
+
+               discard = (x > it->rlw.literal_words) ? it->rlw.literal_words : x;
+
+               it->literal_word_start += discard;
+               it->rlw.literal_words -= discard;
+               x -= discard;
+
+               if (x > 0 || rlwit_word_size(it) == 0) {
+                       if (!next_word(it))
+                               break;
+
+                       it->literal_word_start =
+                               rlwit_literal_words(it) + it->rlw.literal_word_offset;
+               }
+       }
+}
+
+size_t rlwit_discharge(
+       struct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate)
+{
+       size_t index = 0;
+
+       while (index < max && rlwit_word_size(it) > 0) {
+               size_t pd, pl = it->rlw.running_len;
+
+               if (index + pl > max)
+                       pl = max - index;
+
+               ewah_add_empty_words(out, it->rlw.running_bit ^ negate, pl);
+               index += pl;
+
+               pd = it->rlw.literal_words;
+               if (pd + index > max)
+                       pd = max - index;
+
+               ewah_add_dirty_words(out,
+                       it->buffer + it->literal_word_start, pd, negate);
+
+               rlwit_discard_first_words(it, pd + pl);
+               index += pd;
+       }
+
+       return index;
+}
+
+void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out)
+{
+       while (rlwit_word_size(it) > 0) {
+               ewah_add_empty_words(out, 0, rlwit_word_size(it));
+               rlwit_discard_first_words(it, rlwit_word_size(it));
+       }
+}
diff --git a/ewah/ewok.h b/ewah/ewok.h
new file mode 100644 (file)
index 0000000..43adeb5
--- /dev/null
@@ -0,0 +1,233 @@
+/**
+ * Copyright 2013, GitHub, Inc
+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,
+ *     David McIntosh, Robert Becho, Google Inc. and Veronika Zenz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __EWOK_BITMAP_H__
+#define __EWOK_BITMAP_H__
+
+#ifndef ewah_malloc
+#      define ewah_malloc xmalloc
+#endif
+#ifndef ewah_realloc
+#      define ewah_realloc xrealloc
+#endif
+#ifndef ewah_calloc
+#      define ewah_calloc xcalloc
+#endif
+
+typedef uint64_t eword_t;
+#define BITS_IN_WORD (sizeof(eword_t) * 8)
+
+/**
+ * Do not use __builtin_popcountll. The GCC implementation
+ * is notoriously slow on all platforms.
+ *
+ * See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36041
+ */
+static inline uint32_t ewah_bit_popcount64(uint64_t x)
+{
+       x = (x & 0x5555555555555555ULL) + ((x >>  1) & 0x5555555555555555ULL);
+       x = (x & 0x3333333333333333ULL) + ((x >>  2) & 0x3333333333333333ULL);
+       x = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x >>  4) & 0x0F0F0F0F0F0F0F0FULL);
+       return (x * 0x0101010101010101ULL) >> 56;
+}
+
+#ifdef __GNUC__
+#define ewah_bit_ctz64(x) __builtin_ctzll(x)
+#else
+static inline int ewah_bit_ctz64(uint64_t x)
+{
+       int n = 0;
+       if ((x & 0xffffffff) == 0) { x >>= 32; n += 32; }
+       if ((x &     0xffff) == 0) { x >>= 16; n += 16; }
+       if ((x &       0xff) == 0) { x >>=  8; n +=  8; }
+       if ((x &        0xf) == 0) { x >>=  4; n +=  4; }
+       if ((x &        0x3) == 0) { x >>=  2; n +=  2; }
+       if ((x &        0x1) == 0) { x >>=  1; n +=  1; }
+       return n + !x;
+}
+#endif
+
+struct ewah_bitmap {
+       eword_t *buffer;
+       size_t buffer_size;
+       size_t alloc_size;
+       size_t bit_size;
+       eword_t *rlw;
+};
+
+typedef void (*ewah_callback)(size_t pos, void *);
+
+struct ewah_bitmap *ewah_pool_new(void);
+void ewah_pool_free(struct ewah_bitmap *self);
+
+/**
+ * Allocate a new EWAH Compressed bitmap
+ */
+struct ewah_bitmap *ewah_new(void);
+
+/**
+ * Clear all the bits in the bitmap. Does not free or resize
+ * memory.
+ */
+void ewah_clear(struct ewah_bitmap *self);
+
+/**
+ * Free all the memory of the bitmap
+ */
+void ewah_free(struct ewah_bitmap *self);
+
+int ewah_serialize_to(struct ewah_bitmap *self,
+                     int (*write_fun)(void *out, const void *buf, size_t len),
+                     void *out);
+int ewah_serialize(struct ewah_bitmap *self, int fd);
+int ewah_serialize_native(struct ewah_bitmap *self, int fd);
+
+int ewah_deserialize(struct ewah_bitmap *self, int fd);
+int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len);
+int ewah_read_mmap_native(struct ewah_bitmap *self, void *map, size_t len);
+
+uint32_t ewah_checksum(struct ewah_bitmap *self);
+
+/**
+ * Logical not (bitwise negation) in-place on the bitmap
+ *
+ * This operation is linear time based on the size of the bitmap.
+ */
+void ewah_not(struct ewah_bitmap *self);
+
+/**
+ * Call the given callback with the position of every single bit
+ * that has been set on the bitmap.
+ *
+ * This is an efficient operation that does not fully decompress
+ * the bitmap.
+ */
+void ewah_each_bit(struct ewah_bitmap *self, ewah_callback callback, void *payload);
+
+/**
+ * Set a given bit on the bitmap.
+ *
+ * The bit at position `pos` will be set to true. Because of the
+ * way that the bitmap is compressed, a set bit cannot be unset
+ * later on.
+ *
+ * Furthermore, since the bitmap uses streaming compression, bits
+ * can only set incrementally.
+ *
+ * E.g.
+ *             ewah_set(bitmap, 1); // ok
+ *             ewah_set(bitmap, 76); // ok
+ *             ewah_set(bitmap, 77); // ok
+ *             ewah_set(bitmap, 8712800127); // ok
+ *             ewah_set(bitmap, 25); // failed, assert raised
+ */
+void ewah_set(struct ewah_bitmap *self, size_t i);
+
+struct ewah_iterator {
+       const eword_t *buffer;
+       size_t buffer_size;
+
+       size_t pointer;
+       eword_t compressed, literals;
+       eword_t rl, lw;
+       int b;
+};
+
+/**
+ * Initialize a new iterator to run through the bitmap in uncompressed form.
+ *
+ * The iterator can be stack allocated. The underlying bitmap must not be freed
+ * before the iteration is over.
+ *
+ * E.g.
+ *
+ *             struct ewah_bitmap *bitmap = ewah_new();
+ *             struct ewah_iterator it;
+ *
+ *             ewah_iterator_init(&it, bitmap);
+ */
+void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent);
+
+/**
+ * Yield every single word in the bitmap in uncompressed form. This is:
+ * yield single words (32-64 bits) where each bit represents an actual
+ * bit from the bitmap.
+ *
+ * Return: true if a word was yield, false if there are no words left
+ */
+int ewah_iterator_next(eword_t *next, struct ewah_iterator *it);
+
+void ewah_or(
+       struct ewah_bitmap *ewah_i,
+       struct ewah_bitmap *ewah_j,
+       struct ewah_bitmap *out);
+
+void ewah_and_not(
+       struct ewah_bitmap *ewah_i,
+       struct ewah_bitmap *ewah_j,
+       struct ewah_bitmap *out);
+
+void ewah_xor(
+       struct ewah_bitmap *ewah_i,
+       struct ewah_bitmap *ewah_j,
+       struct ewah_bitmap *out);
+
+void ewah_and(
+       struct ewah_bitmap *ewah_i,
+       struct ewah_bitmap *ewah_j,
+       struct ewah_bitmap *out);
+
+/**
+ * Direct word access
+ */
+size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number);
+void ewah_add_dirty_words(
+       struct ewah_bitmap *self, const eword_t *buffer, size_t number, int negate);
+size_t ewah_add(struct ewah_bitmap *self, eword_t word);
+
+
+/**
+ * Uncompressed, old-school bitmap that can be efficiently compressed
+ * into an `ewah_bitmap`.
+ */
+struct bitmap {
+       eword_t *words;
+       size_t word_alloc;
+};
+
+struct bitmap *bitmap_new(void);
+void bitmap_set(struct bitmap *self, size_t pos);
+void bitmap_clear(struct bitmap *self, size_t pos);
+int bitmap_get(struct bitmap *self, size_t pos);
+void bitmap_reset(struct bitmap *self);
+void bitmap_free(struct bitmap *self);
+int bitmap_equals(struct bitmap *self, struct bitmap *other);
+int bitmap_is_subset(struct bitmap *self, struct bitmap *super);
+
+struct ewah_bitmap * bitmap_to_ewah(struct bitmap *bitmap);
+struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah);
+
+void bitmap_and_not(struct bitmap *self, struct bitmap *other);
+void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);
+void bitmap_or(struct bitmap *self, const struct bitmap *other);
+
+void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data);
+size_t bitmap_popcount(struct bitmap *self);
+
+#endif
diff --git a/ewah/ewok_rlw.h b/ewah/ewok_rlw.h
new file mode 100644 (file)
index 0000000..63efdf9
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * Copyright 2013, GitHub, Inc
+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,
+ *     David McIntosh, Robert Becho, Google Inc. and Veronika Zenz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __EWOK_RLW_H__
+#define __EWOK_RLW_H__
+
+#define RLW_RUNNING_BITS (sizeof(eword_t) * 4)
+#define RLW_LITERAL_BITS (sizeof(eword_t) * 8 - 1 - RLW_RUNNING_BITS)
+
+#define RLW_LARGEST_RUNNING_COUNT (((eword_t)1 << RLW_RUNNING_BITS) - 1)
+#define RLW_LARGEST_LITERAL_COUNT (((eword_t)1 << RLW_LITERAL_BITS) - 1)
+
+#define RLW_LARGEST_RUNNING_COUNT_SHIFT (RLW_LARGEST_RUNNING_COUNT << 1)
+
+#define RLW_RUNNING_LEN_PLUS_BIT (((eword_t)1 << (RLW_RUNNING_BITS + 1)) - 1)
+
+static int rlw_get_run_bit(const eword_t *word)
+{
+       return *word & (eword_t)1;
+}
+
+static inline void rlw_set_run_bit(eword_t *word, int b)
+{
+       if (b) {
+               *word |= (eword_t)1;
+       } else {
+               *word &= (eword_t)(~1);
+       }
+}
+
+static inline void rlw_xor_run_bit(eword_t *word)
+{
+       if (*word & 1) {
+               *word &= (eword_t)(~1);
+       } else {
+               *word |= (eword_t)1;
+       }
+}
+
+static inline void rlw_set_running_len(eword_t *word, eword_t l)
+{
+       *word |= RLW_LARGEST_RUNNING_COUNT_SHIFT;
+       *word &= (l << 1) | (~RLW_LARGEST_RUNNING_COUNT_SHIFT);
+}
+
+static inline eword_t rlw_get_running_len(const eword_t *word)
+{
+       return (*word >> 1) & RLW_LARGEST_RUNNING_COUNT;
+}
+
+static inline eword_t rlw_get_literal_words(const eword_t *word)
+{
+       return *word >> (1 + RLW_RUNNING_BITS);
+}
+
+static inline void rlw_set_literal_words(eword_t *word, eword_t l)
+{
+       *word |= ~RLW_RUNNING_LEN_PLUS_BIT;
+       *word &= (l << (RLW_RUNNING_BITS + 1)) | RLW_RUNNING_LEN_PLUS_BIT;
+}
+
+static inline eword_t rlw_size(const eword_t *self)
+{
+       return rlw_get_running_len(self) + rlw_get_literal_words(self);
+}
+
+struct rlw_iterator {
+       const eword_t *buffer;
+       size_t size;
+       size_t pointer;
+       size_t literal_word_start;
+
+       struct {
+               const eword_t *word;
+               int literal_words;
+               int running_len;
+               int literal_word_offset;
+               int running_bit;
+       } rlw;
+};
+
+void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *bitmap);
+void rlwit_discard_first_words(struct rlw_iterator *it, size_t x);
+size_t rlwit_discharge(
+       struct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate);
+void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out);
+
+static inline size_t rlwit_word_size(struct rlw_iterator *it)
+{
+       return it->rlw.running_len + it->rlw.literal_words;
+}
+
+static inline size_t rlwit_literal_words(struct rlw_iterator *it)
+{
+       return it->pointer - it->rlw.literal_words;
+}
+
+#endif
index 90fdd49821a1d6d3e104124444e37ac4b9b03fa2..f061f1fe85ea20549f860c4bf37980941f480833 100644 (file)
@@ -439,7 +439,8 @@ static int find_common(struct fetch_pack_args *args,
        }
        strbuf_release(&req_buf);
 
-       consume_shallow_list(args, fd[0]);
+       if (!got_ready || !no_done)
+               consume_shallow_list(args, fd[0]);
        while (flushes || multi_ack) {
                int ack = get_ack(fd[0], result_sha1);
                if (ack) {
index bbea43075be030c272ef0d8e4cb9c25a179d4e53..78517f254f7e772145ff419bb3f000507458ff60 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -4,6 +4,7 @@
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
+OPTIONS_STUCKLONG=t
 OPTIONS_SPEC="\
 git am [options] [(<mbox>|<Maildir>)...]
 git am [options] (--continue | --skip | --abort)
@@ -37,6 +38,7 @@ abort           restore the original branch and abort the patching operation.
 committer-date-is-author-date    lie about committer date
 ignore-date     use current timestamp for author date
 rerere-autoupdate update the index with reused conflict resolution if possible
+S,gpg-sign?     GPG-sign commits
 rebasing*       (internal use for git-rebase)"
 
 . git-sh-setup
@@ -374,6 +376,7 @@ git_apply_opt=
 committer_date_is_author_date=
 ignore_date=
 allow_rerere_autoupdate=
+gpg_sign_opt=
 
 if test "$(git config --bool --get am.keepcr)" = true
 then
@@ -413,14 +416,14 @@ it will be removed. Please do not use it anymore."
                abort=t ;;
        --rebasing)
                rebasing=t threeway=t ;;
-       --resolvemsg)
-               shift; resolvemsg=$1 ;;
-       --whitespace|--directory|--exclude|--include)
-               git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
-       -C|-p)
-               git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
-       --patch-format)
-               shift ; patch_format="$1" ;;
+       --resolvemsg=*)
+               resolvemsg="${1#--resolvemsg=}" ;;
+       --whitespace=*|--directory=*|--exclude=*|--include=*)
+               git_apply_opt="$git_apply_opt $(sq "$1")" ;;
+       -C*|-p*)
+               git_apply_opt="$git_apply_opt $(sq "$1")" ;;
+       --patch-format=*)
+               patch_format="${1#--patch-format=}" ;;
        --reject|--ignore-whitespace|--ignore-space-change)
                git_apply_opt="$git_apply_opt $1" ;;
        --committer-date-is-author-date)
@@ -435,6 +438,10 @@ it will be removed. Please do not use it anymore."
                keepcr=t ;;
        --no-keep-cr)
                keepcr=f ;;
+       --gpg-sign)
+               gpg_sign_opt=-S ;;
+       --gpg-sign=*)
+               gpg_sign_opt="-S${1#--gpg-sign=}" ;;
        --)
                shift; break ;;
        *)
@@ -899,7 +906,8 @@ did you forget to use 'git add'?"
                        GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
                        export GIT_COMMITTER_DATE
                fi &&
-               git commit-tree $tree ${parent:+-p} $parent <"$dotest/final-commit"
+               git commit-tree ${parent:+-p} $parent ${gpg_sign_opt:+"$gpg_sign_opt"} $tree  \
+                       <"$dotest/final-commit"
        ) &&
        git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
        stop_here $this
index cbd86c37f51c00679498107d1a05131c19afa36e..082eb0d1b7ddbf831a5754d617f1c7aafaa04ae6 100644 (file)
@@ -351,9 +351,7 @@ extern void set_error_routine(void (*routine)(const char *err, va_list params));
 extern void set_die_is_recursing_routine(int (*routine)(void));
 
 extern int starts_with(const char *str, const char *prefix);
-extern int prefixcmp(const char *str, const char *prefix);
 extern int ends_with(const char *str, const char *suffix);
-extern int suffixcmp(const char *str, const char *suffix);
 
 static inline const char *skip_prefix(const char *str, const char *prefix)
 {
@@ -480,9 +478,15 @@ extern FILE *git_fopen(const char*, const char*);
 #endif
 
 #ifdef SNPRINTF_RETURNS_BOGUS
+#ifdef snprintf
+#undef snprintf
+#endif
 #define snprintf git_snprintf
 extern int git_snprintf(char *str, size_t maxsize,
                        const char *format, ...);
+#ifdef vsnprintf
+#undef vsnprintf
+#endif
 #define vsnprintf git_vsnprintf
 extern int git_vsnprintf(char *str, size_t maxsize,
                         const char *format, va_list ap);
index e93a2386754342ce759b568fc360488119c09a2d..4aa3eb80fd25a803d291631965d009a49cfe79dc 100755 (executable)
@@ -5,6 +5,7 @@
 
 PERL='@@PERL@@'
 OPTIONS_KEEPDASHDASH=
+OPTIONS_STUCKLONG=
 OPTIONS_SPEC="\
 git instaweb [options] (--start | --stop | --restart)
 --
index 0a5aa2c82187c3caa4b2a0716369314fc8c80c55..6cd8ebc534c174dfe613f62047424dc7491d75fa 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
-USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...'
+USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff|--ff-only] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...'
 LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
@@ -52,6 +52,21 @@ if test -z "$rebase"
 then
        rebase=$(bool_or_string_config pull.rebase)
 fi
+
+# Setup default fast-forward options via `pull.ff`
+pull_ff=$(git config pull.ff)
+case "$pull_ff" in
+false)
+       no_ff=--no-ff
+       break
+       ;;
+only)
+       ff_only=--ff-only
+       break
+       ;;
+esac
+
+
 dry_run=
 while :
 do
@@ -138,6 +153,15 @@ do
        --no-verify-signatures)
                verify_signatures=--no-verify-signatures
                ;;
+       --gpg-sign|-S)
+               gpg_sign_args=-S
+               ;;
+       --gpg-sign=*)
+               gpg_sign_args=$(git rev-parse --sq-quote "-S${1#--gpg-sign=}")
+               ;;
+       -S*)
+               gpg_sign_args=$(git rev-parse --sq-quote "$1")
+               ;;
        --d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
                dry_run=--dry-run
                ;;
@@ -305,11 +329,13 @@ merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 case "$rebase" in
 true)
        eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity"
+       eval="$eval $gpg_sign_args"
        eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
        ;;
 *)
        eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only"
-       eval="$eval  $log_arg $strategy_args $merge_args $verbosity $progress"
+       eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress"
+       eval="$eval $gpg_sign_args"
        eval="$eval \"\$merge_name\" HEAD $merge_head"
        ;;
 esac
index 8e17525dd86aa614000d9b335f9671a829678901..167d79fea809b918e81c2228ee27baa5fab23db4 100755 (executable)
@@ -1,5 +1,6 @@
 #!/bin/sh
 OPTIONS_KEEPDASHDASH=
+OPTIONS_STUCKLONG=
 OPTIONS_SPEC="\
 git quiltimport [options]
 --
index a4f683a5d70213151f3713cc0a2d76dfe8293fd3..df46f4ca9629253238e94ae31ae26c77b452dde3 100644 (file)
@@ -6,7 +6,8 @@
 
 case "$action" in
 continue)
-       git am --resolved --resolvemsg="$resolvemsg" &&
+       git am --resolved --resolvemsg="$resolvemsg" \
+               ${gpg_sign_opt:+"$gpg_sign_opt"} &&
        move_to_original_branch
        return
        ;;
@@ -26,7 +27,7 @@ then
        # empty commits and even if it didn't the format doesn't really lend
        # itself well to recording empty patches.  fortunately, cherry-pick
        # makes this easy
-       git cherry-pick --allow-empty "$revisions"
+       git cherry-pick ${gpg_sign_opt:+"$gpg_sign_opt"} --allow-empty "$revisions"
        ret=$?
 else
        rm -f "$GIT_DIR/rebased-patches"
@@ -60,7 +61,8 @@ else
                return $?
        fi
 
-       git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" <"$GIT_DIR/rebased-patches"
+       git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
+               ${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches"
        ret=$?
 
        rm -f "$GIT_DIR/rebased-patches"
index 43c19e0829ca727501ba7e4d29c952bc286ccb77..a1adae83131b5918ec6f994043f3f3ccd3b8c3d8 100644 (file)
@@ -179,9 +179,10 @@ exit_with_patch () {
        echo "$1" > "$state_dir"/stopped-sha
        make_patch $1
        git rev-parse --verify HEAD > "$amend"
+       gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
        warn "You can amend the commit now, with"
        warn
-       warn "  git commit --amend"
+       warn "  git commit --amend $gpg_sign_opt_quoted"
        warn
        warn "Once you are satisfied with your changes, run"
        warn
@@ -248,7 +249,9 @@ pick_one () {
 
        test -d "$rewritten" &&
                pick_one_preserving_merges "$@" && return
-       output eval git cherry-pick "$strategy_args" $empty_args $ff "$@"
+       output eval git cherry-pick \
+                       ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
+                       "$strategy_args" $empty_args $ff "$@"
 }
 
 pick_one_preserving_merges () {
@@ -351,7 +354,8 @@ pick_one_preserving_merges () {
                        new_parents=${new_parents# $first_parent}
                        merge_args="--no-log --no-ff"
                        if ! do_with_author output eval \
-                       'git merge $merge_args $strategy_args -m "$msg_content" $new_parents'
+                       'git merge ${gpg_sign_opt:+"$gpg_sign_opt"} \
+                               $merge_args $strategy_args -m "$msg_content" $new_parents'
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
                                die_with_patch $sha1 "Error redoing merge $sha1"
@@ -359,7 +363,9 @@ pick_one_preserving_merges () {
                        echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
                        ;;
                *)
-                       output eval git cherry-pick "$strategy_args" "$@" ||
+                       output eval git cherry-pick \
+                               ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
+                               "$strategy_args" "$@" ||
                                die_with_patch $sha1 "Could not pick $sha1"
                        ;;
                esac
@@ -470,7 +476,8 @@ do_pick () {
                           --no-post-rewrite -n -q -C $1 &&
                        pick_one -n $1 &&
                        git commit --allow-empty --allow-empty-message \
-                                  --amend --no-post-rewrite -n -q -C $1 ||
+                                  --amend --no-post-rewrite -n -q -C $1 \
+                                  ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                        die_with_patch $1 "Could not apply $1... $2"
        else
                pick_one $1 ||
@@ -497,7 +504,7 @@ do_next () {
 
                mark_action_done
                do_pick $sha1 "$rest"
-               git commit --amend --no-post-rewrite || {
+               git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} || {
                        warn "Could not amend commit after successfully picking $sha1... $rest"
                        warn "This is most likely due to an empty commit message, or the pre-commit hook"
                        warn "failed. If the pre-commit hook failed, you may need to resolve the issue before"
@@ -542,19 +549,22 @@ do_next () {
                squash|s|fixup|f)
                        # This is an intermediate commit; its message will only be
                        # used in case of trouble.  So use the long version:
-                       do_with_author output git commit --amend --no-verify -F "$squash_msg" ||
+                       do_with_author output git commit --amend --no-verify -F "$squash_msg" \
+                               ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                die_failed_squash $sha1 "$rest"
                        ;;
                *)
                        # This is the final command of this squash/fixup group
                        if test -f "$fixup_msg"
                        then
-                               do_with_author git commit --amend --no-verify -F "$fixup_msg" ||
+                               do_with_author git commit --amend --no-verify -F "$fixup_msg" \
+                                       ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                        die_failed_squash $sha1 "$rest"
                        else
                                cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
                                rm -f "$GIT_DIR"/MERGE_MSG
-                               do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e ||
+                               do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e \
+                                       ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                        die_failed_squash $sha1 "$rest"
                        fi
                        rm -f "$squash_msg" "$fixup_msg"
@@ -819,14 +829,15 @@ continue)
        else
                if ! test -f "$author_script"
                then
+                       gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
                        die "You have staged changes in your working tree. If these changes are meant to be
 squashed into the previous commit, run:
 
-  git commit --amend
+  git commit --amend $gpg_sign_opt_quoted
 
 If they are meant to go into a new commit, run:
 
-  git commit
+  git commit $gpg_sign_opt_quoted
 
 In both case, once you're done, continue with:
 
@@ -842,10 +853,12 @@ In both case, once you're done, continue with:
                        die "\
 You have uncommitted changes in your working tree. Please, commit them
 first and then run 'git rebase --continue' again."
-                       do_with_author git commit --amend --no-verify -F "$msg" -e ||
+                       do_with_author git commit --amend --no-verify -F "$msg" -e \
+                               ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                die "Could not commit staged changes."
                else
-                       do_with_author git commit --no-verify -F "$msg" -e ||
+                       do_with_author git commit --no-verify -F "$msg" -e \
+                               ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                die "Could not commit staged changes."
                fi
        fi
index e7d96de9adcb133982bbbc6d20cd3b52b3c89214..5381857c59968c38526140d53e3150f19e8d0b0c 100644 (file)
@@ -27,7 +27,7 @@ continue_merge () {
        cmt=`cat "$state_dir/current"`
        if ! git diff-index --quiet --ignore-submodules HEAD --
        then
-               if ! git commit --no-verify -C "$cmt"
+               if ! git commit ${gpg_sign_opt:+"$gpg_sign_opt"} --no-verify -C "$cmt"
                then
                        echo "Commit failed, please do not call \"git commit\""
                        echo "directly, but instead do one of the following: "
index 8a3efa2983d08e38d40ae8b6dcecd82b0901ffa1..5f6732bf3dce78966da1cbb83d4094d584f92598 100755 (executable)
@@ -5,6 +5,7 @@
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
+OPTIONS_STUCKLONG=t
 OPTIONS_SPEC="\
 git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
 git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
@@ -36,6 +37,7 @@ ignore-date!       passed to 'git am'
 whitespace=!       passed to 'git apply'
 ignore-whitespace! passed to 'git apply'
 C=!                passed to 'git apply'
+S,gpg-sign?        GPG-sign commits
  Actions:
 continue!          continue
 abort!             abort and check out the original branch
@@ -84,6 +86,7 @@ preserve_merges=
 autosquash=
 keep_empty=
 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
+gpg_sign_opt=
 
 read_basic_state () {
        test -f "$state_dir/head-name" &&
@@ -106,6 +109,8 @@ read_basic_state () {
                strategy_opts="$(cat "$state_dir"/strategy_opts)"
        test -f "$state_dir"/allow_rerere_autoupdate &&
                allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
+       test -f "$state_dir"/gpg_sign_opt &&
+               gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)"
 }
 
 write_basic_state () {
@@ -119,6 +124,7 @@ write_basic_state () {
                "$state_dir"/strategy_opts
        test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
                "$state_dir"/allow_rerere_autoupdate
+       test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt
 }
 
 output () {
@@ -236,23 +242,19 @@ do
                test $total_argc -eq 2 || usage
                action=${1##--}
                ;;
-       --onto)
-               test 2 -le "$#" || usage
-               onto="$2"
-               shift
+       --onto=*)
+               onto="${1#--onto=}"
                ;;
-       -x)
-               test 2 -le "$#" || usage
-               cmd="${cmd}exec $2${LF}"
-               shift
+       --exec=*)
+               cmd="${cmd}exec ${1#--exec=}${LF}"
                ;;
-       -i)
+       --interactive)
                interactive_rebase=explicit
                ;;
-       -k)
+       --keep-empty)
                keep_empty=yes
                ;;
-       -p)
+       --preserve-merges)
                preserve_merges=t
                test -z "$interactive_rebase" && interactive_rebase=implied
                ;;
@@ -268,21 +270,19 @@ do
        --no-fork-point)
                fork_point=
                ;;
-       -M|-m)
+       --merge)
                do_merge=t
                ;;
-       -X)
-               shift
-               strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")"
+       --strategy-option=*)
+               strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}")"
                do_merge=t
                test -z "$strategy" && strategy=recursive
                ;;
-       -s)
-               shift
-               strategy="$1"
+       --strategy=*)
+               strategy="${1#--strategy=}"
                do_merge=t
                ;;
-       -n)
+       --no-stat)
                diffstat=
                ;;
        --stat)
@@ -291,21 +291,20 @@ do
        --autostash)
                autostash=true
                ;;
-       -v)
+       --verbose)
                verbose=t
                diffstat=t
                GIT_QUIET=
                ;;
-       -q)
+       --quiet)
                GIT_QUIET=t
                git_am_opt="$git_am_opt -q"
                verbose=
                diffstat=
                ;;
-       --whitespace)
-               shift
-               git_am_opt="$git_am_opt --whitespace=$1"
-               case "$1" in
+       --whitespace=*)
+               git_am_opt="$git_am_opt --whitespace=${1#--whitespace=}"
+               case "${1#--whitespace=}" in
                fix|strip)
                        force_rebase=t
                        ;;
@@ -318,19 +317,24 @@ do
                git_am_opt="$git_am_opt $1"
                force_rebase=t
                ;;
-       -C)
-               shift
-               git_am_opt="$git_am_opt -C$1"
+       -C*)
+               git_am_opt="$git_am_opt $1"
                ;;
        --root)
                rebase_root=t
                ;;
-       -f|--no-ff)
+       --force-rebase|--no-ff)
                force_rebase=t
                ;;
        --rerere-autoupdate|--no-rerere-autoupdate)
                allow_rerere_autoupdate="$1"
                ;;
+       --gpg-sign)
+               gpg_sign_opt=-S
+               ;;
+       --gpg-sign=*)
+               gpg_sign_opt="-S${1#--gpg-sign=}"
+               ;;
        --)
                shift
                break
index fe21d5db631cac88a52d765e5052d4f725aad69e..cf4f1505a54bb6d2fdd580ab7e8186dc8103de49 100755 (executable)
@@ -9,6 +9,7 @@ LONG_USAGE='Summarizes the changes between two commits to the standard output,
 and includes the given URL in the generated summary.'
 SUBDIRECTORY_OK='Yes'
 OPTIONS_KEEPDASHDASH=
+OPTIONS_STUCKLONG=
 OPTIONS_SPEC='git request-pull [options] start url [end]
 --
 p    show patch text as well
index fffa3c72d75961159888fd156614aa6679f3638c..5f28b32dc7ff75830bde919b2f4776f7a9b017e2 100644 (file)
@@ -72,6 +72,8 @@ if test -n "$OPTIONS_SPEC"; then
        parseopt_extra=
        [ -n "$OPTIONS_KEEPDASHDASH" ] &&
                parseopt_extra="--keep-dashdash"
+       [ -n "$OPTIONS_STUCKLONG" ] &&
+               parseopt_extra="$parseopt_extra --stuck-long"
 
        eval "$(
                echo "$OPTIONS_SPEC" |
index 4a30087768a16d77bfcec41889f58b64fdf5f89c..a33f68d27c0e1d482562373e6fd299da5dd9361a 100755 (executable)
@@ -241,6 +241,15 @@ module_name()
 #
 # Clone a submodule
 #
+# $1 = submodule path
+# $2 = submodule name
+# $3 = URL to clone
+# $4 = reference repository to reuse (empty for independent)
+# $5 = depth argument for shallow clones (empty for deep)
+# $6 = (remote-tracking) starting point for the local branch (empty for HEAD)
+# $7 = local branch to create (empty for a detached HEAD, unless $6 is
+#      also empty, in which case the local branch is left unchanged)
+#
 # Prior to calling, cmd_update checks that a possibly existing
 # path is not a git repository.
 # Likewise, cmd_add checks that path does not exist at all,
@@ -253,6 +262,8 @@ module_clone()
        url=$3
        reference="$4"
        depth="$5"
+       start_point="$6"
+       local_branch="$7"
        quiet=
        if test -n "$GIT_QUIET"
        then
@@ -306,7 +317,16 @@ module_clone()
        echo "gitdir: $rel/$a" >"$sm_path/.git"
 
        rel=$(echo $a | sed -e 's|[^/][^/]*|..|g')
-       (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
+       (
+               clear_local_git_env
+               cd "$sm_path" &&
+               GIT_WORK_TREE=. git config core.worktree "$rel/$b" &&
+               # ash fails to wordsplit ${local_branch:+-B "$local_branch"...}
+               case "$local_branch" in
+               '') git checkout -f -q ${start_point:+"$start_point"} ;;
+               ?*) git checkout -f -q -B "$local_branch" ${start_point:+"$start_point"} ;;
+               esac
+       ) || die "$(eval_gettext "Unable to setup cloned submodule '\$sm_path'")"
 }
 
 isnumber()
@@ -469,16 +489,15 @@ Use -f if you really want to add it." >&2
                                echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
                        fi
                fi
-               module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" || exit
-               (
-                       clear_local_git_env
-                       cd "$sm_path" &&
-                       # ash fails to wordsplit ${branch:+-b "$branch"...}
-                       case "$branch" in
-                       '') git checkout -f -q ;;
-                       ?*) git checkout -f -q -B "$branch" "origin/$branch" ;;
-                       esac
-               ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"
+               if test -n "$branch"
+               then
+                       start_point="origin/$branch"
+                       local_branch="$branch"
+               else
+                       start_point=""
+                       local_branch=""
+               fi
+               module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" "$start_point" "$local_branch" || exit
        fi
        git config submodule."$sm_name".url "$realrepo"
 
@@ -799,32 +818,35 @@ cmd_update()
                fi
                name=$(module_name "$sm_path") || exit
                url=$(git config submodule."$name".url)
-               branch=$(get_submodule_config "$name" branch master)
+               config_branch=$(get_submodule_config "$name" branch)
+               branch="${config_branch:-master}"
+               local_branch="$branch"
                if ! test -z "$update"
                then
                        update_module=$update
                else
                        update_module=$(git config submodule."$name".update)
-                       case "$update_module" in
-                       '')
-                               ;; # Unset update mode
-                       checkout | rebase | merge | none)
-                               ;; # Known update modes
-                       !*)
-                               ;; # Custom update command
-                       *)
-                               die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
-                               ;;
-                       esac
+                       if test -z "$update_module"
+                       then
+                               update_module="checkout"
+                       fi
                fi
 
                displaypath=$(relative_path "$prefix$sm_path")
 
-               if test "$update_module" = "none"
-               then
+               case "$update_module" in
+               none)
                        echo "Skipping submodule '$displaypath'"
                        continue
-               fi
+                       ;;
+               checkout)
+                       local_branch=""
+                       ;;
+               rebase | merge | !*)
+                       ;;
+               *)
+                       die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
+               esac
 
                if test -z "$url"
                then
@@ -838,7 +860,8 @@ Maybe you want to use 'update --init'?")"
 
                if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
                then
-                       module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
+                       start_point="origin/${branch}"
+                       module_clone "$sm_path" "$name" "$url" "$reference" "$depth" "$start_point" "$local_branch" || exit
                        cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
@@ -884,11 +907,16 @@ Maybe you want to use 'update --init'?")"
                        case ";$cloned_modules;" in
                        *";$name;"*)
                                # then there is no local change to integrate
-                               update_module= ;;
+                               update_module='!git reset --hard -q'
                        esac
 
                        must_die_on_failure=
                        case "$update_module" in
+                       checkout)
+                               command="git checkout $subforce -q"
+                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
+                               say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
+                               ;;
                        rebase)
                                command="git rebase"
                                die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$displaypath'")"
@@ -908,10 +936,7 @@ Maybe you want to use 'update --init'?")"
                                must_die_on_failure=yes
                                ;;
                        *)
-                               command="git checkout $subforce -q"
-                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
-                               say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
-                               ;;
+                               die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
                        esac
 
                        if (clear_local_git_env; cd "$sm_path" && $command "$sha1")
diff --git a/hash.c b/hash.c
deleted file mode 100644 (file)
index 749ecfe..0000000
--- a/hash.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Some generic hashing helpers.
- */
-#include "cache.h"
-#include "hash.h"
-
-/*
- * Look up a hash entry in the hash table. Return the pointer to
- * the existing entry, or the empty slot if none existed. The caller
- * can then look at the (*ptr) to see whether it existed or not.
- */
-static struct hash_table_entry *lookup_hash_entry(unsigned int hash, const struct hash_table *table)
-{
-       unsigned int size = table->size, nr = hash % size;
-       struct hash_table_entry *array = table->array;
-
-       while (array[nr].ptr) {
-               if (array[nr].hash == hash)
-                       break;
-               nr++;
-               if (nr >= size)
-                       nr = 0;
-       }
-       return array + nr;
-}
-
-
-/*
- * Insert a new hash entry pointer into the table.
- *
- * If that hash entry already existed, return the pointer to
- * the existing entry (and the caller can create a list of the
- * pointers or do anything else). If it didn't exist, return
- * NULL (and the caller knows the pointer has been inserted).
- */
-static void **insert_hash_entry(unsigned int hash, void *ptr, struct hash_table *table)
-{
-       struct hash_table_entry *entry = lookup_hash_entry(hash, table);
-
-       if (!entry->ptr) {
-               entry->ptr = ptr;
-               entry->hash = hash;
-               table->nr++;
-               return NULL;
-       }
-       return &entry->ptr;
-}
-
-static void grow_hash_table(struct hash_table *table)
-{
-       unsigned int i;
-       unsigned int old_size = table->size, new_size;
-       struct hash_table_entry *old_array = table->array, *new_array;
-
-       new_size = alloc_nr(old_size);
-       new_array = xcalloc(sizeof(struct hash_table_entry), new_size);
-       table->size = new_size;
-       table->array = new_array;
-       table->nr = 0;
-       for (i = 0; i < old_size; i++) {
-               unsigned int hash = old_array[i].hash;
-               void *ptr = old_array[i].ptr;
-               if (ptr)
-                       insert_hash_entry(hash, ptr, table);
-       }
-       free(old_array);
-}
-
-void *lookup_hash(unsigned int hash, const struct hash_table *table)
-{
-       if (!table->array)
-               return NULL;
-       return lookup_hash_entry(hash, table)->ptr;
-}
-
-void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table)
-{
-       unsigned int nr = table->nr;
-       if (nr >= table->size/2)
-               grow_hash_table(table);
-       return insert_hash_entry(hash, ptr, table);
-}
-
-int for_each_hash(const struct hash_table *table, int (*fn)(void *, void *), void *data)
-{
-       int sum = 0;
-       unsigned int i;
-       unsigned int size = table->size;
-       struct hash_table_entry *array = table->array;
-
-       for (i = 0; i < size; i++) {
-               void *ptr = array->ptr;
-               array++;
-               if (ptr) {
-                       int val = fn(ptr, data);
-                       if (val < 0)
-                               return val;
-                       sum += val;
-               }
-       }
-       return sum;
-}
-
-void free_hash(struct hash_table *table)
-{
-       free(table->array);
-       table->array = NULL;
-       table->size = 0;
-       table->nr = 0;
-}
diff --git a/hash.h b/hash.h
deleted file mode 100644 (file)
index 1d43ac0..0000000
--- a/hash.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef HASH_H
-#define HASH_H
-
-/*
- * These are some simple generic hash table helper functions.
- * Not necessarily suitable for all users, but good for things
- * where you want to just keep track of a list of things, and
- * have a good hash to use on them.
- *
- * It keeps the hash table at roughly 50-75% free, so the memory
- * cost of the hash table itself is roughly
- *
- *     3 * 2*sizeof(void *) * nr_of_objects
- *
- * bytes.
- *
- * FIXME: on 64-bit architectures, we waste memory. It would be
- * good to have just 32-bit pointers, requiring a special allocator
- * for hashed entries or something.
- */
-struct hash_table_entry {
-       unsigned int hash;
-       void *ptr;
-};
-
-struct hash_table {
-       unsigned int size, nr;
-       struct hash_table_entry *array;
-};
-
-extern void *lookup_hash(unsigned int hash, const struct hash_table *table);
-extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table);
-extern int for_each_hash(const struct hash_table *table, int (*fn)(void *, void *), void *data);
-extern void free_hash(struct hash_table *table);
-
-static inline void init_hash(struct hash_table *table)
-{
-       table->size = 0;
-       table->nr = 0;
-       table->array = NULL;
-}
-
-static inline void preallocate_hash(struct hash_table *table, unsigned int elts)
-{
-       assert(table->size == 0 && table->nr == 0 && table->array == NULL);
-       table->size = elts * 2;
-       table->array = xcalloc(sizeof(struct hash_table_entry), table->size);
-}
-
-#endif
diff --git a/hashmap.c b/hashmap.c
new file mode 100644 (file)
index 0000000..d1b8056
--- /dev/null
+++ b/hashmap.c
@@ -0,0 +1,228 @@
+/*
+ * Generic implementation of hash-based key value mappings.
+ */
+#include "cache.h"
+#include "hashmap.h"
+
+#define FNV32_BASE ((unsigned int) 0x811c9dc5)
+#define FNV32_PRIME ((unsigned int) 0x01000193)
+
+unsigned int strhash(const char *str)
+{
+       unsigned int c, hash = FNV32_BASE;
+       while ((c = (unsigned char) *str++))
+               hash = (hash * FNV32_PRIME) ^ c;
+       return hash;
+}
+
+unsigned int strihash(const char *str)
+{
+       unsigned int c, hash = FNV32_BASE;
+       while ((c = (unsigned char) *str++)) {
+               if (c >= 'a' && c <= 'z')
+                       c -= 'a' - 'A';
+               hash = (hash * FNV32_PRIME) ^ c;
+       }
+       return hash;
+}
+
+unsigned int memhash(const void *buf, size_t len)
+{
+       unsigned int hash = FNV32_BASE;
+       unsigned char *ucbuf = (unsigned char *) buf;
+       while (len--) {
+               unsigned int c = *ucbuf++;
+               hash = (hash * FNV32_PRIME) ^ c;
+       }
+       return hash;
+}
+
+unsigned int memihash(const void *buf, size_t len)
+{
+       unsigned int hash = FNV32_BASE;
+       unsigned char *ucbuf = (unsigned char *) buf;
+       while (len--) {
+               unsigned int c = *ucbuf++;
+               if (c >= 'a' && c <= 'z')
+                       c -= 'a' - 'A';
+               hash = (hash * FNV32_PRIME) ^ c;
+       }
+       return hash;
+}
+
+#define HASHMAP_INITIAL_SIZE 64
+/* grow / shrink by 2^2 */
+#define HASHMAP_RESIZE_BITS 2
+/* load factor in percent */
+#define HASHMAP_LOAD_FACTOR 80
+
+static void alloc_table(struct hashmap *map, unsigned int size)
+{
+       map->tablesize = size;
+       map->table = xcalloc(size, sizeof(struct hashmap_entry *));
+
+       /* calculate resize thresholds for new size */
+       map->grow_at = (unsigned int) ((uint64_t) size * HASHMAP_LOAD_FACTOR / 100);
+       if (size <= HASHMAP_INITIAL_SIZE)
+               map->shrink_at = 0;
+       else
+               /*
+                * The shrink-threshold must be slightly smaller than
+                * (grow-threshold / resize-factor) to prevent erratic resizing,
+                * thus we divide by (resize-factor + 1).
+                */
+               map->shrink_at = map->grow_at / ((1 << HASHMAP_RESIZE_BITS) + 1);
+}
+
+static inline int entry_equals(const struct hashmap *map,
+               const struct hashmap_entry *e1, const struct hashmap_entry *e2,
+               const void *keydata)
+{
+       return (e1 == e2) || (e1->hash == e2->hash && !map->cmpfn(e1, e2, keydata));
+}
+
+static inline unsigned int bucket(const struct hashmap *map,
+               const struct hashmap_entry *key)
+{
+       return key->hash & (map->tablesize - 1);
+}
+
+static void rehash(struct hashmap *map, unsigned int newsize)
+{
+       unsigned int i, oldsize = map->tablesize;
+       struct hashmap_entry **oldtable = map->table;
+
+       alloc_table(map, newsize);
+       for (i = 0; i < oldsize; i++) {
+               struct hashmap_entry *e = oldtable[i];
+               while (e) {
+                       struct hashmap_entry *next = e->next;
+                       unsigned int b = bucket(map, e);
+                       e->next = map->table[b];
+                       map->table[b] = e;
+                       e = next;
+               }
+       }
+       free(oldtable);
+}
+
+static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map,
+               const struct hashmap_entry *key, const void *keydata)
+{
+       struct hashmap_entry **e = &map->table[bucket(map, key)];
+       while (*e && !entry_equals(map, *e, key, keydata))
+               e = &(*e)->next;
+       return e;
+}
+
+static int always_equal(const void *unused1, const void *unused2, const void *unused3)
+{
+       return 0;
+}
+
+void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
+               size_t initial_size)
+{
+       unsigned int size = HASHMAP_INITIAL_SIZE;
+       map->size = 0;
+       map->cmpfn = equals_function ? equals_function : always_equal;
+
+       /* calculate initial table size and allocate the table */
+       initial_size = (unsigned int) ((uint64_t) initial_size * 100
+                       / HASHMAP_LOAD_FACTOR);
+       while (initial_size > size)
+               size <<= HASHMAP_RESIZE_BITS;
+       alloc_table(map, size);
+}
+
+void hashmap_free(struct hashmap *map, int free_entries)
+{
+       if (!map || !map->table)
+               return;
+       if (free_entries) {
+               struct hashmap_iter iter;
+               struct hashmap_entry *e;
+               hashmap_iter_init(map, &iter);
+               while ((e = hashmap_iter_next(&iter)))
+                       free(e);
+       }
+       free(map->table);
+       memset(map, 0, sizeof(*map));
+}
+
+void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata)
+{
+       return *find_entry_ptr(map, key, keydata);
+}
+
+void *hashmap_get_next(const struct hashmap *map, const void *entry)
+{
+       struct hashmap_entry *e = ((struct hashmap_entry *) entry)->next;
+       for (; e; e = e->next)
+               if (entry_equals(map, entry, e, NULL))
+                       return e;
+       return NULL;
+}
+
+void hashmap_add(struct hashmap *map, void *entry)
+{
+       unsigned int b = bucket(map, entry);
+
+       /* add entry */
+       ((struct hashmap_entry *) entry)->next = map->table[b];
+       map->table[b] = entry;
+
+       /* fix size and rehash if appropriate */
+       map->size++;
+       if (map->size > map->grow_at)
+               rehash(map, map->tablesize << HASHMAP_RESIZE_BITS);
+}
+
+void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)
+{
+       struct hashmap_entry *old;
+       struct hashmap_entry **e = find_entry_ptr(map, key, keydata);
+       if (!*e)
+               return NULL;
+
+       /* remove existing entry */
+       old = *e;
+       *e = old->next;
+       old->next = NULL;
+
+       /* fix size and rehash if appropriate */
+       map->size--;
+       if (map->size < map->shrink_at)
+               rehash(map, map->tablesize >> HASHMAP_RESIZE_BITS);
+       return old;
+}
+
+void *hashmap_put(struct hashmap *map, void *entry)
+{
+       struct hashmap_entry *old = hashmap_remove(map, entry, NULL);
+       hashmap_add(map, entry);
+       return old;
+}
+
+void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)
+{
+       iter->map = map;
+       iter->tablepos = 0;
+       iter->next = NULL;
+}
+
+void *hashmap_iter_next(struct hashmap_iter *iter)
+{
+       struct hashmap_entry *current = iter->next;
+       for (;;) {
+               if (current) {
+                       iter->next = current->next;
+                       return current;
+               }
+
+               if (iter->tablepos >= iter->map->tablesize)
+                       return NULL;
+
+               current = iter->map->table[iter->tablepos++];
+       }
+}
diff --git a/hashmap.h b/hashmap.h
new file mode 100644 (file)
index 0000000..a816ad4
--- /dev/null
+++ b/hashmap.h
@@ -0,0 +1,71 @@
+#ifndef HASHMAP_H
+#define HASHMAP_H
+
+/*
+ * Generic implementation of hash-based key-value mappings.
+ * See Documentation/technical/api-hashmap.txt.
+ */
+
+/* FNV-1 functions */
+
+extern unsigned int strhash(const char *buf);
+extern unsigned int strihash(const char *buf);
+extern unsigned int memhash(const void *buf, size_t len);
+extern unsigned int memihash(const void *buf, size_t len);
+
+/* data structures */
+
+struct hashmap_entry {
+       struct hashmap_entry *next;
+       unsigned int hash;
+};
+
+typedef int (*hashmap_cmp_fn)(const void *entry, const void *entry_or_key,
+               const void *keydata);
+
+struct hashmap {
+       struct hashmap_entry **table;
+       hashmap_cmp_fn cmpfn;
+       unsigned int size, tablesize, grow_at, shrink_at;
+};
+
+struct hashmap_iter {
+       struct hashmap *map;
+       struct hashmap_entry *next;
+       unsigned int tablepos;
+};
+
+/* hashmap functions */
+
+extern void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
+               size_t initial_size);
+extern void hashmap_free(struct hashmap *map, int free_entries);
+
+/* hashmap_entry functions */
+
+static inline void hashmap_entry_init(void *entry, unsigned int hash)
+{
+       struct hashmap_entry *e = entry;
+       e->hash = hash;
+       e->next = NULL;
+}
+extern void *hashmap_get(const struct hashmap *map, const void *key,
+               const void *keydata);
+extern void *hashmap_get_next(const struct hashmap *map, const void *entry);
+extern void hashmap_add(struct hashmap *map, void *entry);
+extern void *hashmap_put(struct hashmap *map, void *entry);
+extern void *hashmap_remove(struct hashmap *map, const void *key,
+               const void *keydata);
+
+/* hashmap_iter functions */
+
+extern void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
+extern void *hashmap_iter_next(struct hashmap_iter *iter);
+static inline void *hashmap_iter_first(struct hashmap *map,
+               struct hashmap_iter *iter)
+{
+       hashmap_iter_init(map, iter);
+       return hashmap_iter_next(iter);
+}
+
+#endif
diff --git a/http.c b/http.c
index 70eaa26e88cbcfa62e7d5e967b4e519432319210..1212c587afbc4df6100f6a099939550eabeb5ebf 100644 (file)
--- a/http.c
+++ b/http.c
@@ -880,6 +880,20 @@ int handle_curl_result(struct slot_results *results)
        }
 }
 
+int run_one_slot(struct active_request_slot *slot,
+                struct slot_results *results)
+{
+       slot->results = results;
+       if (!start_active_slot(slot)) {
+               snprintf(curl_errorstr, sizeof(curl_errorstr),
+                        "failed to start HTTP request");
+               return HTTP_START_FAILED;
+       }
+
+       run_active_slot(slot);
+       return handle_curl_result(results);
+}
+
 static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
 {
        char *ptr;
@@ -907,7 +921,6 @@ static int http_request(const char *url,
        int ret;
 
        slot = get_active_slot();
-       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
 
        if (result == NULL) {
@@ -942,14 +955,7 @@ static int http_request(const char *url,
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
 
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               ret = handle_curl_result(&results);
-       } else {
-               snprintf(curl_errorstr, sizeof(curl_errorstr),
-                        "failed to start HTTP request");
-               ret = HTTP_START_FAILED;
-       }
+       ret = run_one_slot(slot, &results);
 
        if (options && options->content_type)
                curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
diff --git a/http.h b/http.h
index cd37d5881cb0e63e52bd33d0b49bed49645340f5..a82888445e9f04acfe27e36b1f274ffb9659cddf 100644 (file)
--- a/http.h
+++ b/http.h
@@ -90,6 +90,15 @@ extern void finish_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
 extern int handle_curl_result(struct slot_results *results);
 
+/*
+ * This will run one slot to completion in a blocking manner, similar to how
+ * curl_easy_perform would work (but we don't want to use that, because
+ * we do not want to intermingle calls to curl_multi and curl_easy).
+ *
+ */
+int run_one_slot(struct active_request_slot *slot,
+                struct slot_results *results);
+
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);
 extern void add_fill_function(void *data, int (*fill)(void *));
diff --git a/khash.h b/khash.h
new file mode 100644 (file)
index 0000000..57ff603
--- /dev/null
+++ b/khash.h
@@ -0,0 +1,338 @@
+/* The MIT License
+
+   Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+   SOFTWARE.
+*/
+
+#ifndef __AC_KHASH_H
+#define __AC_KHASH_H
+
+#define AC_VERSION_KHASH_H "0.2.8"
+
+typedef uint32_t khint32_t;
+typedef uint64_t khint64_t;
+
+typedef khint32_t khint_t;
+typedef khint_t khiter_t;
+
+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
+
+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
+
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+
+static inline khint_t __ac_X31_hash_string(const char *s)
+{
+       khint_t h = (khint_t)*s;
+       if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
+       return h;
+}
+
+#define kh_str_hash_func(key) __ac_X31_hash_string(key)
+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
+
+static const double __ac_HASH_UPPER = 0.77;
+
+#define __KHASH_TYPE(name, khkey_t, khval_t) \
+       typedef struct { \
+               khint_t n_buckets, size, n_occupied, upper_bound; \
+               khint32_t *flags; \
+               khkey_t *keys; \
+               khval_t *vals; \
+       } kh_##name##_t;
+
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)                                             \
+       extern kh_##name##_t *kh_init_##name(void);                                                     \
+       extern void kh_destroy_##name(kh_##name##_t *h);                                        \
+       extern void kh_clear_##name(kh_##name##_t *h);                                          \
+       extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key);      \
+       extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+       extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+       extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+
+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+       SCOPE kh_##name##_t *kh_init_##name(void) {                                                     \
+               return (kh_##name##_t*)xcalloc(1, sizeof(kh_##name##_t));               \
+       }                                                                                                                                       \
+       SCOPE void kh_destroy_##name(kh_##name##_t *h)                                          \
+       {                                                                                                                                       \
+               if (h) {                                                                                                                \
+                       free((void *)h->keys); free(h->flags);                                  \
+                       free((void *)h->vals);                                                                          \
+                       free(h);                                                                                                        \
+               }                                                                                                                               \
+       }                                                                                                                                       \
+       SCOPE void kh_clear_##name(kh_##name##_t *h)                                            \
+       {                                                                                                                                       \
+               if (h && h->flags) {                                                                                    \
+                       memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
+                       h->size = h->n_occupied = 0;                                                            \
+               }                                                                                                                               \
+       }                                                                                                                                       \
+       SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key)        \
+       {                                                                                                                                       \
+               if (h->n_buckets) {                                                                                             \
+                       khint_t k, i, last, mask, step = 0; \
+                       mask = h->n_buckets - 1;                                                                        \
+                       k = __hash_func(key); i = k & mask;                                                     \
+                       last = i; \
+                       while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+                               i = (i + (++step)) & mask; \
+                               if (i == last) return h->n_buckets;                                             \
+                       }                                                                                                                       \
+                       return __ac_iseither(h->flags, i)? h->n_buckets : i;            \
+               } else return 0;                                                                                                \
+       }                                                                                                                                       \
+       SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
+       { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+               khint32_t *new_flags = NULL;                                                                            \
+               khint_t j = 1;                                                                                                  \
+               {                                                                                                                               \
+                       kroundup32(new_n_buckets);                                                                      \
+                       if (new_n_buckets < 4) new_n_buckets = 4;                                       \
+                       if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
+                       else { /* hash table size to be changed (shrink or expand); rehash */ \
+                               new_flags = (khint32_t*)xmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+                               if (!new_flags) return -1;                                                              \
+                               memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+                               if (h->n_buckets < new_n_buckets) {     /* expand */            \
+                                       khkey_t *new_keys = (khkey_t*)xrealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+                                       if (!new_keys) return -1;                                                       \
+                                       h->keys = new_keys;                                                                     \
+                                       if (kh_is_map) {                                                                        \
+                                               khval_t *new_vals = (khval_t*)xrealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+                                               if (!new_vals) return -1;                                               \
+                                               h->vals = new_vals;                                                             \
+                                       }                                                                                                       \
+                               } /* otherwise shrink */                                                                \
+                       }                                                                                                                       \
+               }                                                                                                                               \
+               if (j) { /* rehashing is needed */                                                              \
+                       for (j = 0; j != h->n_buckets; ++j) {                                           \
+                               if (__ac_iseither(h->flags, j) == 0) {                                  \
+                                       khkey_t key = h->keys[j];                                                       \
+                                       khval_t val;                                                                            \
+                                       khint_t new_mask;                                                                       \
+                                       new_mask = new_n_buckets - 1;                                           \
+                                       if (kh_is_map) val = h->vals[j];                                        \
+                                       __ac_set_isdel_true(h->flags, j);                                       \
+                                       while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
+                                               khint_t k, i, step = 0; \
+                                               k = __hash_func(key);                                                   \
+                                               i = k & new_mask;                                                               \
+                                               while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
+                                               __ac_set_isempty_false(new_flags, i);                   \
+                                               if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
+                                                       { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
+                                                       if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
+                                                       __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
+                                               } else { /* write the element and jump out of the loop */ \
+                                                       h->keys[i] = key;                                                       \
+                                                       if (kh_is_map) h->vals[i] = val;                        \
+                                                       break;                                                                          \
+                                               }                                                                                               \
+                                       }                                                                                                       \
+                               }                                                                                                               \
+                       }                                                                                                                       \
+                       if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
+                               h->keys = (khkey_t*)xrealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+                               if (kh_is_map) h->vals = (khval_t*)xrealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+                       }                                                                                                                       \
+                       free(h->flags); /* free the working space */                            \
+                       h->flags = new_flags;                                                                           \
+                       h->n_buckets = new_n_buckets;                                                           \
+                       h->n_occupied = h->size;                                                                        \
+                       h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
+               }                                                                                                                               \
+               return 0;                                                                                                               \
+       }                                                                                                                                       \
+       SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
+       {                                                                                                                                       \
+               khint_t x;                                                                                                              \
+               if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
+                       if (h->n_buckets > (h->size<<1)) {                                                      \
+                               if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
+                                       *ret = -1; return h->n_buckets;                                         \
+                               }                                                                                                               \
+                       } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
+                               *ret = -1; return h->n_buckets;                                                 \
+                       }                                                                                                                       \
+               } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
+               {                                                                                                                               \
+                       khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
+                       x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
+                       if (__ac_isempty(h->flags, i)) x = i; /* for speed up */        \
+                       else {                                                                                                          \
+                               last = i; \
+                               while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+                                       if (__ac_isdel(h->flags, i)) site = i;                          \
+                                       i = (i + (++step)) & mask; \
+                                       if (i == last) { x = site; break; }                                     \
+                               }                                                                                                               \
+                               if (x == h->n_buckets) {                                                                \
+                                       if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
+                                       else x = i;                                                                                     \
+                               }                                                                                                               \
+                       }                                                                                                                       \
+               }                                                                                                                               \
+               if (__ac_isempty(h->flags, x)) { /* not present at all */               \
+                       h->keys[x] = key;                                                                                       \
+                       __ac_set_isboth_false(h->flags, x);                                                     \
+                       ++h->size; ++h->n_occupied;                                                                     \
+                       *ret = 1;                                                                                                       \
+               } else if (__ac_isdel(h->flags, x)) { /* deleted */                             \
+                       h->keys[x] = key;                                                                                       \
+                       __ac_set_isboth_false(h->flags, x);                                                     \
+                       ++h->size;                                                                                                      \
+                       *ret = 2;                                                                                                       \
+               } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
+               return x;                                                                                                               \
+       }                                                                                                                                       \
+       SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)                           \
+       {                                                                                                                                       \
+               if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {                 \
+                       __ac_set_isdel_true(h->flags, x);                                                       \
+                       --h->size;                                                                                                      \
+               }                                                                                                                               \
+       }
+
+#define KHASH_DECLARE(name, khkey_t, khval_t)                                                  \
+       __KHASH_TYPE(name, khkey_t, khval_t)                                                            \
+       __KHASH_PROTOTYPES(name, khkey_t, khval_t)
+
+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+       __KHASH_TYPE(name, khkey_t, khval_t)                                                            \
+       __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)
+
+/* Other convenient macros... */
+
+/*! @function
+  @abstract     Test whether a bucket contains data.
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       1 if containing data; 0 otherwise [int]
+ */
+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
+
+/*! @function
+  @abstract     Get key given an iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       Key [type of keys]
+ */
+#define kh_key(h, x) ((h)->keys[x])
+
+/*! @function
+  @abstract     Get value given an iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       Value [type of values]
+  @discussion   For hash sets, calling this results in segfault.
+ */
+#define kh_val(h, x) ((h)->vals[x])
+
+/*! @function
+  @abstract     Alias of kh_val()
+ */
+#define kh_value(h, x) ((h)->vals[x])
+
+/*! @function
+  @abstract     Get the start iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       The start iterator [khint_t]
+ */
+#define kh_begin(h) (khint_t)(0)
+
+/*! @function
+  @abstract     Get the end iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       The end iterator [khint_t]
+ */
+#define kh_end(h) ((h)->n_buckets)
+
+/*! @function
+  @abstract     Get the number of elements in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       Number of elements in the hash table [khint_t]
+ */
+#define kh_size(h) ((h)->size)
+
+/*! @function
+  @abstract     Get the number of buckets in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       Number of buckets in the hash table [khint_t]
+ */
+#define kh_n_buckets(h) ((h)->n_buckets)
+
+/*! @function
+  @abstract     Iterate over the entries in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  kvar  Variable to which key will be assigned
+  @param  vvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach(h, kvar, vvar, code) { khint_t __i;         \
+       for (__i = kh_begin(h); __i != kh_end(h); ++__i) {              \
+               if (!kh_exist(h,__i)) continue;                                         \
+               (kvar) = kh_key(h,__i);                                                         \
+               (vvar) = kh_val(h,__i);                                                         \
+               code;                                                                                           \
+       } }
+
+/*! @function
+  @abstract     Iterate over the values in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  vvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach_value(h, vvar, code) { khint_t __i;         \
+       for (__i = kh_begin(h); __i != kh_end(h); ++__i) {              \
+               if (!kh_exist(h,__i)) continue;                                         \
+               (vvar) = kh_val(h,__i);                                                         \
+               code;                                                                                           \
+       } }
+
+static inline khint_t __kh_oid_hash(const unsigned char *oid)
+{
+       khint_t hash;
+       memcpy(&hash, oid, sizeof(hash));
+       return hash;
+}
+
+#define __kh_oid_cmp(a, b) (hashcmp(a, b) == 0)
+
+KHASH_INIT(sha1, const unsigned char *, void *, 1, __kh_oid_hash, __kh_oid_cmp)
+typedef kh_sha1_t khash_sha1;
+
+KHASH_INIT(sha1_pos, const unsigned char *, int, 1, __kh_oid_hash, __kh_oid_cmp)
+typedef kh_sha1_pos_t khash_sha1_pos;
+
+#endif /* __AC_KHASH_H */
index 717638b333b680da7a3a5acefcb73db9d956a00f..150010105806291180d9e6bdfcf1939fb242f2ac 100644 (file)
@@ -766,16 +766,6 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
        }
 }
 
-static void load_tree_desc(struct tree_desc *desc, void **tree,
-                          const unsigned char *sha1)
-{
-       unsigned long size;
-       *tree = read_object_with_reference(sha1, tree_type, &size, NULL);
-       if (!*tree)
-               die("Unable to read tree (%s)", sha1_to_hex(sha1));
-       init_tree_desc(desc, *tree, size);
-}
-
 static int count_parents(struct commit *commit)
 {
        struct commit_list *parents = commit->parents;
@@ -842,18 +832,11 @@ static void queue_diffs(struct line_log_data *range,
                        struct diff_queue_struct *queue,
                        struct commit *commit, struct commit *parent)
 {
-       void *tree1 = NULL, *tree2 = NULL;
-       struct tree_desc desc1, desc2;
-
        assert(commit);
-       load_tree_desc(&desc2, &tree2, commit->tree->object.sha1);
-       if (parent)
-               load_tree_desc(&desc1, &tree1, parent->tree->object.sha1);
-       else
-               init_tree_desc(&desc1, "", 0);
 
        DIFF_QUEUE_CLEAR(&diff_queued_diff);
-       diff_tree(&desc1, &desc2, "", opt);
+       diff_tree_sha1(parent ? parent->tree->object.sha1 : NULL,
+                       commit->tree->object.sha1, "", opt);
        if (opt->detect_rename) {
                filter_diffs_for_paths(range, 1);
                if (diff_might_be_rename())
@@ -861,11 +844,6 @@ static void queue_diffs(struct line_log_data *range,
                filter_diffs_for_paths(range, 0);
        }
        move_diff_queue(queue, &diff_queued_diff);
-
-       if (tree1)
-               free(tree1);
-       if (tree2)
-               free(tree2);
 }
 
 static char *get_nth_line(long line, unsigned long *ends, void *data)
index 8400a8e937d8303ecc2ace1136a0700187865f58..41770929420da4cba934b0b86b9cd2f55f9cab31 100644 (file)
@@ -201,7 +201,9 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
                const char *path, int stage, int refresh, int options)
 {
        struct cache_entry *ce;
-       ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
+       ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage,
+                             (refresh ? (CE_MATCH_REFRESH |
+                                         CE_MATCH_IGNORE_MISSING) : 0 ));
        if (!ce)
                return error(_("addinfo_cache failed for path '%s'"), path);
        return add_cache_entry(ce, options);
index e5b6e1ad239bac6914fecfb9071d208e1e6a11cc..97444d02010e13de1eab9abe6f1fc71287658faf 100644 (file)
@@ -8,49 +8,28 @@
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
 
-/*
- * This removes bit 5 if bit 6 is set.
- *
- * That will make US-ASCII characters hash to their upper-case
- * equivalent. We could easily do this one whole word at a time,
- * but that's for future worries.
- */
-static inline unsigned char icase_hash(unsigned char c)
-{
-       return c & ~((c & 0x40) >> 1);
-}
-
-static unsigned int hash_name(const char *name, int namelen)
-{
-       unsigned int hash = 0x123;
-
-       while (namelen--) {
-               unsigned char c = *name++;
-               c = icase_hash(c);
-               hash = hash*101 + c;
-       }
-       return hash;
-}
-
 struct dir_entry {
-       struct dir_entry *next;
+       struct hashmap_entry ent;
        struct dir_entry *parent;
        struct cache_entry *ce;
        int nr;
        unsigned int namelen;
 };
 
+static int dir_entry_cmp(const struct dir_entry *e1,
+               const struct dir_entry *e2, const char *name)
+{
+       return e1->namelen != e2->namelen || strncasecmp(e1->ce->name,
+                       name ? name : e2->ce->name, e1->namelen);
+}
+
 static struct dir_entry *find_dir_entry(struct index_state *istate,
                const char *name, unsigned int namelen)
 {
-       unsigned int hash = hash_name(name, namelen);
-       struct dir_entry *dir;
-
-       for (dir = lookup_hash(hash, &istate->dir_hash); dir; dir = dir->next)
-               if (dir->namelen == namelen &&
-                   !strncasecmp(dir->ce->name, name, namelen))
-                       return dir;
-       return NULL;
+       struct dir_entry key;
+       hashmap_entry_init(&key, memihash(name, namelen));
+       key.namelen = namelen;
+       return hashmap_get(&istate->dir_hash, &key, name);
 }
 
 static struct dir_entry *hash_dir_entry(struct index_state *istate,
@@ -84,18 +63,11 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,
        dir = find_dir_entry(istate, ce->name, namelen);
        if (!dir) {
                /* not found, create it and add to hash table */
-               void **pdir;
-               unsigned int hash = hash_name(ce->name, namelen);
-
                dir = xcalloc(1, sizeof(struct dir_entry));
+               hashmap_entry_init(dir, memihash(ce->name, namelen));
                dir->namelen = namelen;
                dir->ce = ce;
-
-               pdir = insert_hash(hash, dir, &istate->dir_hash);
-               if (pdir) {
-                       dir->next = *pdir;
-                       *pdir = dir;
-               }
+               hashmap_add(&istate->dir_hash, dir);
 
                /* recursively add missing parent directories */
                dir->parent = hash_dir_entry(istate, ce, namelen);
@@ -114,45 +86,50 @@ static void add_dir_entry(struct index_state *istate, struct cache_entry *ce)
 static void remove_dir_entry(struct index_state *istate, struct cache_entry *ce)
 {
        /*
-        * Release reference to the directory entry (and parents if 0).
-        *
-        * Note: we do not remove / free the entry because there's no
-        * hash.[ch]::remove_hash and dir->next may point to other entries
-        * that are still valid, so we must not free the memory.
+        * Release reference to the directory entry. If 0, remove and continue
+        * with parent directory.
         */
        struct dir_entry *dir = hash_dir_entry(istate, ce, ce_namelen(ce));
-       while (dir && dir->nr && !(--dir->nr))
-               dir = dir->parent;
+       while (dir && !(--dir->nr)) {
+               struct dir_entry *parent = dir->parent;
+               hashmap_remove(&istate->dir_hash, dir, NULL);
+               free(dir);
+               dir = parent;
+       }
 }
 
 static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
 {
-       void **pos;
-       unsigned int hash;
-
        if (ce->ce_flags & CE_HASHED)
                return;
        ce->ce_flags |= CE_HASHED;
-       ce->next = NULL;
-       hash = hash_name(ce->name, ce_namelen(ce));
-       pos = insert_hash(hash, ce, &istate->name_hash);
-       if (pos) {
-               ce->next = *pos;
-               *pos = ce;
-       }
+       hashmap_entry_init(ce, memihash(ce->name, ce_namelen(ce)));
+       hashmap_add(&istate->name_hash, ce);
 
-       if (ignore_case && !(ce->ce_flags & CE_UNHASHED))
+       if (ignore_case)
                add_dir_entry(istate, ce);
 }
 
+static int cache_entry_cmp(const struct cache_entry *ce1,
+               const struct cache_entry *ce2, const void *remove)
+{
+       /*
+        * For remove_name_hash, find the exact entry (pointer equality); for
+        * index_file_exists, find all entries with matching hash code and
+        * decide whether the entry matches in same_name.
+        */
+       return remove ? !(ce1 == ce2) : 0;
+}
+
 static void lazy_init_name_hash(struct index_state *istate)
 {
        int nr;
 
        if (istate->name_hash_initialized)
                return;
-       if (istate->cache_nr)
-               preallocate_hash(&istate->name_hash, istate->cache_nr);
+       hashmap_init(&istate->name_hash, (hashmap_cmp_fn) cache_entry_cmp,
+                       istate->cache_nr);
+       hashmap_init(&istate->dir_hash, (hashmap_cmp_fn) dir_entry_cmp, 0);
        for (nr = 0; nr < istate->cache_nr; nr++)
                hash_index_entry(istate, istate->cache[nr]);
        istate->name_hash_initialized = 1;
@@ -160,31 +137,19 @@ static void lazy_init_name_hash(struct index_state *istate)
 
 void add_name_hash(struct index_state *istate, struct cache_entry *ce)
 {
-       /* if already hashed, add reference to directory entries */
-       if (ignore_case && (ce->ce_flags & CE_STATE_MASK) == CE_STATE_MASK)
-               add_dir_entry(istate, ce);
-
-       ce->ce_flags &= ~CE_UNHASHED;
        if (istate->name_hash_initialized)
                hash_index_entry(istate, ce);
 }
 
-/*
- * We don't actually *remove* it, we can just mark it invalid so that
- * we won't find it in lookups.
- *
- * Not only would we have to search the lists (simple enough), but
- * we'd also have to rehash other hash buckets in case this makes the
- * hash bucket empty (common). So it's much better to just mark
- * it.
- */
 void remove_name_hash(struct index_state *istate, struct cache_entry *ce)
 {
-       /* if already hashed, release reference to directory entries */
-       if (ignore_case && (ce->ce_flags & CE_STATE_MASK) == CE_HASHED)
-               remove_dir_entry(istate, ce);
+       if (!istate->name_hash_initialized || !(ce->ce_flags & CE_HASHED))
+               return;
+       ce->ce_flags &= ~CE_HASHED;
+       hashmap_remove(&istate->name_hash, ce, ce);
 
-       ce->ce_flags |= CE_UNHASHED;
+       if (ignore_case)
+               remove_dir_entry(istate, ce);
 }
 
 static int slow_same_name(const char *name1, int len1, const char *name2, int len2)
@@ -247,49 +212,27 @@ struct cache_entry *index_dir_exists(struct index_state *istate, const char *nam
 
 struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase)
 {
-       unsigned int hash = hash_name(name, namelen);
        struct cache_entry *ce;
+       struct hashmap_entry key;
 
        lazy_init_name_hash(istate);
-       ce = lookup_hash(hash, &istate->name_hash);
 
+       hashmap_entry_init(&key, memihash(name, namelen));
+       ce = hashmap_get(&istate->name_hash, &key, NULL);
        while (ce) {
-               if (!(ce->ce_flags & CE_UNHASHED)) {
-                       if (same_name(ce, name, namelen, icase))
-                               return ce;
-               }
-               ce = ce->next;
+               if (same_name(ce, name, namelen, icase))
+                       return ce;
+               ce = hashmap_get_next(&istate->name_hash, ce);
        }
        return NULL;
 }
 
-struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
-{
-       if (namelen > 0 && name[namelen - 1] == '/')
-               return index_dir_exists(istate, name, namelen - 1);
-       return index_file_exists(istate, name, namelen, icase);
-}
-
-static int free_dir_entry(void *entry, void *unused)
-{
-       struct dir_entry *dir = entry;
-       while (dir) {
-               struct dir_entry *next = dir->next;
-               free(dir);
-               dir = next;
-       }
-       return 0;
-}
-
 void free_name_hash(struct index_state *istate)
 {
        if (!istate->name_hash_initialized)
                return;
        istate->name_hash_initialized = 0;
-       if (ignore_case)
-               /* free directory entries */
-               for_each_hash(&istate->dir_hash, free_dir_entry, NULL);
 
-       free_hash(&istate->name_hash);
-       free_hash(&istate->dir_hash);
+       hashmap_free(&istate->name_hash, 0);
+       hashmap_free(&istate->dir_hash, 1);
 }
index 2975dcd581024cd483953b47636466b79c95bafe..4aa7023903374aa3ccea23d5fc9ae82c9c67bca7 100644 (file)
@@ -75,7 +75,7 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb)
                return 0;
        } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
                if (!v)
-                       config_error_nonbool(k);
+                       return config_error_nonbool(k);
                c->combine = parse_combine_notes_fn(v);
                if (!c->combine) {
                        error(_("Bad notes.rewriteMode value: '%s'"), v);
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
new file mode 100644 (file)
index 0000000..1218bef
--- /dev/null
@@ -0,0 +1,552 @@
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "progress.h"
+#include "pack-revindex.h"
+#include "pack.h"
+#include "pack-bitmap.h"
+#include "sha1-lookup.h"
+#include "pack-objects.h"
+
+struct bitmapped_commit {
+       struct commit *commit;
+       struct ewah_bitmap *bitmap;
+       struct ewah_bitmap *write_as;
+       int flags;
+       int xor_offset;
+       uint32_t commit_pos;
+};
+
+struct bitmap_writer {
+       struct ewah_bitmap *commits;
+       struct ewah_bitmap *trees;
+       struct ewah_bitmap *blobs;
+       struct ewah_bitmap *tags;
+
+       khash_sha1 *bitmaps;
+       khash_sha1 *reused;
+       struct packing_data *to_pack;
+
+       struct bitmapped_commit *selected;
+       unsigned int selected_nr, selected_alloc;
+
+       struct progress *progress;
+       int show_progress;
+       unsigned char pack_checksum[20];
+};
+
+static struct bitmap_writer writer;
+
+void bitmap_writer_show_progress(int show)
+{
+       writer.show_progress = show;
+}
+
+/**
+ * Build the initial type index for the packfile
+ */
+void bitmap_writer_build_type_index(struct pack_idx_entry **index,
+                                   uint32_t index_nr)
+{
+       uint32_t i;
+
+       writer.commits = ewah_new();
+       writer.trees = ewah_new();
+       writer.blobs = ewah_new();
+       writer.tags = ewah_new();
+
+       for (i = 0; i < index_nr; ++i) {
+               struct object_entry *entry = (struct object_entry *)index[i];
+               enum object_type real_type;
+
+               entry->in_pack_pos = i;
+
+               switch (entry->type) {
+               case OBJ_COMMIT:
+               case OBJ_TREE:
+               case OBJ_BLOB:
+               case OBJ_TAG:
+                       real_type = entry->type;
+                       break;
+
+               default:
+                       real_type = sha1_object_info(entry->idx.sha1, NULL);
+                       break;
+               }
+
+               switch (real_type) {
+               case OBJ_COMMIT:
+                       ewah_set(writer.commits, i);
+                       break;
+
+               case OBJ_TREE:
+                       ewah_set(writer.trees, i);
+                       break;
+
+               case OBJ_BLOB:
+                       ewah_set(writer.blobs, i);
+                       break;
+
+               case OBJ_TAG:
+                       ewah_set(writer.tags, i);
+                       break;
+
+               default:
+                       die("Missing type information for %s (%d/%d)",
+                           sha1_to_hex(entry->idx.sha1), real_type, entry->type);
+               }
+       }
+}
+
+/**
+ * Compute the actual bitmaps
+ */
+static struct object **seen_objects;
+static unsigned int seen_objects_nr, seen_objects_alloc;
+
+static inline void push_bitmapped_commit(struct commit *commit, struct ewah_bitmap *reused)
+{
+       if (writer.selected_nr >= writer.selected_alloc) {
+               writer.selected_alloc = (writer.selected_alloc + 32) * 2;
+               writer.selected = xrealloc(writer.selected,
+                                          writer.selected_alloc * sizeof(struct bitmapped_commit));
+       }
+
+       writer.selected[writer.selected_nr].commit = commit;
+       writer.selected[writer.selected_nr].bitmap = reused;
+       writer.selected[writer.selected_nr].flags = 0;
+
+       writer.selected_nr++;
+}
+
+static inline void mark_as_seen(struct object *object)
+{
+       ALLOC_GROW(seen_objects, seen_objects_nr + 1, seen_objects_alloc);
+       seen_objects[seen_objects_nr++] = object;
+}
+
+static inline void reset_all_seen(void)
+{
+       unsigned int i;
+       for (i = 0; i < seen_objects_nr; ++i) {
+               seen_objects[i]->flags &= ~(SEEN | ADDED | SHOWN);
+       }
+       seen_objects_nr = 0;
+}
+
+static uint32_t find_object_pos(const unsigned char *sha1)
+{
+       struct object_entry *entry = packlist_find(writer.to_pack, sha1, NULL);
+
+       if (!entry) {
+               die("Failed to write bitmap index. Packfile doesn't have full closure "
+                       "(object %s is missing)", sha1_to_hex(sha1));
+       }
+
+       return entry->in_pack_pos;
+}
+
+static void show_object(struct object *object, const struct name_path *path,
+                       const char *last, void *data)
+{
+       struct bitmap *base = data;
+       bitmap_set(base, find_object_pos(object->sha1));
+       mark_as_seen(object);
+}
+
+static void show_commit(struct commit *commit, void *data)
+{
+       mark_as_seen((struct object *)commit);
+}
+
+static int
+add_to_include_set(struct bitmap *base, struct commit *commit)
+{
+       khiter_t hash_pos;
+       uint32_t bitmap_pos = find_object_pos(commit->object.sha1);
+
+       if (bitmap_get(base, bitmap_pos))
+               return 0;
+
+       hash_pos = kh_get_sha1(writer.bitmaps, commit->object.sha1);
+       if (hash_pos < kh_end(writer.bitmaps)) {
+               struct bitmapped_commit *bc = kh_value(writer.bitmaps, hash_pos);
+               bitmap_or_ewah(base, bc->bitmap);
+               return 0;
+       }
+
+       bitmap_set(base, bitmap_pos);
+       return 1;
+}
+
+static int
+should_include(struct commit *commit, void *_data)
+{
+       struct bitmap *base = _data;
+
+       if (!add_to_include_set(base, commit)) {
+               struct commit_list *parent = commit->parents;
+
+               mark_as_seen((struct object *)commit);
+
+               while (parent) {
+                       parent->item->object.flags |= SEEN;
+                       mark_as_seen((struct object *)parent->item);
+                       parent = parent->next;
+               }
+
+               return 0;
+       }
+
+       return 1;
+}
+
+static void compute_xor_offsets(void)
+{
+       static const int MAX_XOR_OFFSET_SEARCH = 10;
+
+       int i, next = 0;
+
+       while (next < writer.selected_nr) {
+               struct bitmapped_commit *stored = &writer.selected[next];
+
+               int best_offset = 0;
+               struct ewah_bitmap *best_bitmap = stored->bitmap;
+               struct ewah_bitmap *test_xor;
+
+               for (i = 1; i <= MAX_XOR_OFFSET_SEARCH; ++i) {
+                       int curr = next - i;
+
+                       if (curr < 0)
+                               break;
+
+                       test_xor = ewah_pool_new();
+                       ewah_xor(writer.selected[curr].bitmap, stored->bitmap, test_xor);
+
+                       if (test_xor->buffer_size < best_bitmap->buffer_size) {
+                               if (best_bitmap != stored->bitmap)
+                                       ewah_pool_free(best_bitmap);
+
+                               best_bitmap = test_xor;
+                               best_offset = i;
+                       } else {
+                               ewah_pool_free(test_xor);
+                       }
+               }
+
+               stored->xor_offset = best_offset;
+               stored->write_as = best_bitmap;
+
+               next++;
+       }
+}
+
+void bitmap_writer_build(struct packing_data *to_pack)
+{
+       static const double REUSE_BITMAP_THRESHOLD = 0.2;
+
+       int i, reuse_after, need_reset;
+       struct bitmap *base = bitmap_new();
+       struct rev_info revs;
+
+       writer.bitmaps = kh_init_sha1();
+       writer.to_pack = to_pack;
+
+       if (writer.show_progress)
+               writer.progress = start_progress("Building bitmaps", writer.selected_nr);
+
+       init_revisions(&revs, NULL);
+       revs.tag_objects = 1;
+       revs.tree_objects = 1;
+       revs.blob_objects = 1;
+       revs.no_walk = 0;
+
+       revs.include_check = should_include;
+       reset_revision_walk();
+
+       reuse_after = writer.selected_nr * REUSE_BITMAP_THRESHOLD;
+       need_reset = 0;
+
+       for (i = writer.selected_nr - 1; i >= 0; --i) {
+               struct bitmapped_commit *stored;
+               struct object *object;
+
+               khiter_t hash_pos;
+               int hash_ret;
+
+               stored = &writer.selected[i];
+               object = (struct object *)stored->commit;
+
+               if (stored->bitmap == NULL) {
+                       if (i < writer.selected_nr - 1 &&
+                           (need_reset ||
+                            !in_merge_bases(writer.selected[i + 1].commit,
+                                            stored->commit))) {
+                           bitmap_reset(base);
+                           reset_all_seen();
+                       }
+
+                       add_pending_object(&revs, object, "");
+                       revs.include_check_data = base;
+
+                       if (prepare_revision_walk(&revs))
+                               die("revision walk setup failed");
+
+                       traverse_commit_list(&revs, show_commit, show_object, base);
+
+                       revs.pending.nr = 0;
+                       revs.pending.alloc = 0;
+                       revs.pending.objects = NULL;
+
+                       stored->bitmap = bitmap_to_ewah(base);
+                       need_reset = 0;
+               } else
+                       need_reset = 1;
+
+               if (i >= reuse_after)
+                       stored->flags |= BITMAP_FLAG_REUSE;
+
+               hash_pos = kh_put_sha1(writer.bitmaps, object->sha1, &hash_ret);
+               if (hash_ret == 0)
+                       die("Duplicate entry when writing index: %s",
+                           sha1_to_hex(object->sha1));
+
+               kh_value(writer.bitmaps, hash_pos) = stored;
+               display_progress(writer.progress, writer.selected_nr - i);
+       }
+
+       bitmap_free(base);
+       stop_progress(&writer.progress);
+
+       compute_xor_offsets();
+}
+
+/**
+ * Select the commits that will be bitmapped
+ */
+static inline unsigned int next_commit_index(unsigned int idx)
+{
+       static const unsigned int MIN_COMMITS = 100;
+       static const unsigned int MAX_COMMITS = 5000;
+
+       static const unsigned int MUST_REGION = 100;
+       static const unsigned int MIN_REGION = 20000;
+
+       unsigned int offset, next;
+
+       if (idx <= MUST_REGION)
+               return 0;
+
+       if (idx <= MIN_REGION) {
+               offset = idx - MUST_REGION;
+               return (offset < MIN_COMMITS) ? offset : MIN_COMMITS;
+       }
+
+       offset = idx - MIN_REGION;
+       next = (offset < MAX_COMMITS) ? offset : MAX_COMMITS;
+
+       return (next > MIN_COMMITS) ? next : MIN_COMMITS;
+}
+
+static int date_compare(const void *_a, const void *_b)
+{
+       struct commit *a = *(struct commit **)_a;
+       struct commit *b = *(struct commit **)_b;
+       return (long)b->date - (long)a->date;
+}
+
+void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack)
+{
+       if (prepare_bitmap_git() < 0)
+               return;
+
+       writer.reused = kh_init_sha1();
+       rebuild_existing_bitmaps(to_pack, writer.reused, writer.show_progress);
+}
+
+static struct ewah_bitmap *find_reused_bitmap(const unsigned char *sha1)
+{
+       khiter_t hash_pos;
+
+       if (!writer.reused)
+               return NULL;
+
+       hash_pos = kh_get_sha1(writer.reused, sha1);
+       if (hash_pos >= kh_end(writer.reused))
+               return NULL;
+
+       return kh_value(writer.reused, hash_pos);
+}
+
+void bitmap_writer_select_commits(struct commit **indexed_commits,
+                                 unsigned int indexed_commits_nr,
+                                 int max_bitmaps)
+{
+       unsigned int i = 0, j, next;
+
+       qsort(indexed_commits, indexed_commits_nr, sizeof(indexed_commits[0]),
+             date_compare);
+
+       if (writer.show_progress)
+               writer.progress = start_progress("Selecting bitmap commits", 0);
+
+       if (indexed_commits_nr < 100) {
+               for (i = 0; i < indexed_commits_nr; ++i)
+                       push_bitmapped_commit(indexed_commits[i], NULL);
+               return;
+       }
+
+       for (;;) {
+               struct ewah_bitmap *reused_bitmap = NULL;
+               struct commit *chosen = NULL;
+
+               next = next_commit_index(i);
+
+               if (i + next >= indexed_commits_nr)
+                       break;
+
+               if (max_bitmaps > 0 && writer.selected_nr >= max_bitmaps) {
+                       writer.selected_nr = max_bitmaps;
+                       break;
+               }
+
+               if (next == 0) {
+                       chosen = indexed_commits[i];
+                       reused_bitmap = find_reused_bitmap(chosen->object.sha1);
+               } else {
+                       chosen = indexed_commits[i + next];
+
+                       for (j = 0; j <= next; ++j) {
+                               struct commit *cm = indexed_commits[i + j];
+
+                               reused_bitmap = find_reused_bitmap(cm->object.sha1);
+                               if (reused_bitmap || (cm->object.flags & NEEDS_BITMAP) != 0) {
+                                       chosen = cm;
+                                       break;
+                               }
+
+                               if (cm->parents && cm->parents->next)
+                                       chosen = cm;
+                       }
+               }
+
+               push_bitmapped_commit(chosen, reused_bitmap);
+
+               i += next + 1;
+               display_progress(writer.progress, i);
+       }
+
+       stop_progress(&writer.progress);
+}
+
+
+static int sha1write_ewah_helper(void *f, const void *buf, size_t len)
+{
+       /* sha1write will die on error */
+       sha1write(f, buf, len);
+       return len;
+}
+
+/**
+ * Write the bitmap index to disk
+ */
+static inline void dump_bitmap(struct sha1file *f, struct ewah_bitmap *bitmap)
+{
+       if (ewah_serialize_to(bitmap, sha1write_ewah_helper, f) < 0)
+               die("Failed to write bitmap index");
+}
+
+static const unsigned char *sha1_access(size_t pos, void *table)
+{
+       struct pack_idx_entry **index = table;
+       return index[pos]->sha1;
+}
+
+static void write_selected_commits_v1(struct sha1file *f,
+                                     struct pack_idx_entry **index,
+                                     uint32_t index_nr)
+{
+       int i;
+
+       for (i = 0; i < writer.selected_nr; ++i) {
+               struct bitmapped_commit *stored = &writer.selected[i];
+               struct bitmap_disk_entry on_disk;
+
+               int commit_pos =
+                       sha1_pos(stored->commit->object.sha1, index, index_nr, sha1_access);
+
+               if (commit_pos < 0)
+                       die("BUG: trying to write commit not in index");
+
+               on_disk.object_pos = htonl(commit_pos);
+               on_disk.xor_offset = stored->xor_offset;
+               on_disk.flags = stored->flags;
+
+               sha1write(f, &on_disk, sizeof(on_disk));
+               dump_bitmap(f, stored->write_as);
+       }
+}
+
+static void write_hash_cache(struct sha1file *f,
+                            struct pack_idx_entry **index,
+                            uint32_t index_nr)
+{
+       uint32_t i;
+
+       for (i = 0; i < index_nr; ++i) {
+               struct object_entry *entry = (struct object_entry *)index[i];
+               uint32_t hash_value = htonl(entry->hash);
+               sha1write(f, &hash_value, sizeof(hash_value));
+       }
+}
+
+void bitmap_writer_set_checksum(unsigned char *sha1)
+{
+       hashcpy(writer.pack_checksum, sha1);
+}
+
+void bitmap_writer_finish(struct pack_idx_entry **index,
+                         uint32_t index_nr,
+                         const char *filename,
+                         uint16_t options)
+{
+       static char tmp_file[PATH_MAX];
+       static uint16_t default_version = 1;
+       static uint16_t flags = BITMAP_OPT_FULL_DAG;
+       struct sha1file *f;
+
+       struct bitmap_disk_header header;
+
+       int fd = odb_mkstemp(tmp_file, sizeof(tmp_file), "pack/tmp_bitmap_XXXXXX");
+
+       if (fd < 0)
+               die_errno("unable to create '%s'", tmp_file);
+       f = sha1fd(fd, tmp_file);
+
+       memcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));
+       header.version = htons(default_version);
+       header.options = htons(flags | options);
+       header.entry_count = htonl(writer.selected_nr);
+       memcpy(header.checksum, writer.pack_checksum, 20);
+
+       sha1write(f, &header, sizeof(header));
+       dump_bitmap(f, writer.commits);
+       dump_bitmap(f, writer.trees);
+       dump_bitmap(f, writer.blobs);
+       dump_bitmap(f, writer.tags);
+       write_selected_commits_v1(f, index, index_nr);
+
+       if (options & BITMAP_OPT_HASH_CACHE)
+               write_hash_cache(f, index, index_nr);
+
+       sha1close(f, NULL, CSUM_FSYNC);
+
+       if (adjust_shared_perm(tmp_file))
+               die_errno("unable to make temporary bitmap file readable");
+
+       if (rename(tmp_file, filename))
+               die_errno("unable to rename temporary bitmap file to '%s'", filename);
+}
diff --git a/pack-bitmap.c b/pack-bitmap.c
new file mode 100644 (file)
index 0000000..ae0b57b
--- /dev/null
@@ -0,0 +1,1073 @@
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "diff.h"
+#include "revision.h"
+#include "progress.h"
+#include "list-objects.h"
+#include "pack.h"
+#include "pack-bitmap.h"
+#include "pack-revindex.h"
+#include "pack-objects.h"
+
+/*
+ * An entry on the bitmap index, representing the bitmap for a given
+ * commit.
+ */
+struct stored_bitmap {
+       unsigned char sha1[20];
+       struct ewah_bitmap *root;
+       struct stored_bitmap *xor;
+       int flags;
+};
+
+/*
+ * The currently active bitmap index. By design, repositories only have
+ * a single bitmap index available (the index for the biggest packfile in
+ * the repository), since bitmap indexes need full closure.
+ *
+ * If there is more than one bitmap index available (e.g. because of alternates),
+ * the active bitmap index is the largest one.
+ */
+static struct bitmap_index {
+       /* Packfile to which this bitmap index belongs to */
+       struct packed_git *pack;
+
+       /* reverse index for the packfile */
+       struct pack_revindex *reverse_index;
+
+       /*
+        * Mark the first `reuse_objects` in the packfile as reused:
+        * they will be sent as-is without using them for repacking
+        * calculations
+        */
+       uint32_t reuse_objects;
+
+       /* mmapped buffer of the whole bitmap index */
+       unsigned char *map;
+       size_t map_size; /* size of the mmaped buffer */
+       size_t map_pos; /* current position when loading the index */
+
+       /*
+        * Type indexes.
+        *
+        * Each bitmap marks which objects in the packfile  are of the given
+        * type. This provides type information when yielding the objects from
+        * the packfile during a walk, which allows for better delta bases.
+        */
+       struct ewah_bitmap *commits;
+       struct ewah_bitmap *trees;
+       struct ewah_bitmap *blobs;
+       struct ewah_bitmap *tags;
+
+       /* Map from SHA1 -> `stored_bitmap` for all the bitmapped comits */
+       khash_sha1 *bitmaps;
+
+       /* Number of bitmapped commits */
+       uint32_t entry_count;
+
+       /* Name-hash cache (or NULL if not present). */
+       uint32_t *hashes;
+
+       /*
+        * Extended index.
+        *
+        * When trying to perform bitmap operations with objects that are not
+        * packed in `pack`, these objects are added to this "fake index" and
+        * are assumed to appear at the end of the packfile for all operations
+        */
+       struct eindex {
+               struct object **objects;
+               uint32_t *hashes;
+               uint32_t count, alloc;
+               khash_sha1_pos *positions;
+       } ext_index;
+
+       /* Bitmap result of the last performed walk */
+       struct bitmap *result;
+
+       /* Version of the bitmap index */
+       unsigned int version;
+
+       unsigned loaded : 1;
+
+} bitmap_git;
+
+static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)
+{
+       struct ewah_bitmap *parent;
+       struct ewah_bitmap *composed;
+
+       if (st->xor == NULL)
+               return st->root;
+
+       composed = ewah_pool_new();
+       parent = lookup_stored_bitmap(st->xor);
+       ewah_xor(st->root, parent, composed);
+
+       ewah_pool_free(st->root);
+       st->root = composed;
+       st->xor = NULL;
+
+       return composed;
+}
+
+/*
+ * Read a bitmap from the current read position on the mmaped
+ * index, and increase the read position accordingly
+ */
+static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
+{
+       struct ewah_bitmap *b = ewah_pool_new();
+
+       int bitmap_size = ewah_read_mmap(b,
+               index->map + index->map_pos,
+               index->map_size - index->map_pos);
+
+       if (bitmap_size < 0) {
+               error("Failed to load bitmap index (corrupted?)");
+               ewah_pool_free(b);
+               return NULL;
+       }
+
+       index->map_pos += bitmap_size;
+       return b;
+}
+
+static int load_bitmap_header(struct bitmap_index *index)
+{
+       struct bitmap_disk_header *header = (void *)index->map;
+
+       if (index->map_size < sizeof(*header) + 20)
+               return error("Corrupted bitmap index (missing header data)");
+
+       if (memcmp(header->magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE)) != 0)
+               return error("Corrupted bitmap index file (wrong header)");
+
+       index->version = ntohs(header->version);
+       if (index->version != 1)
+               return error("Unsupported version for bitmap index file (%d)", index->version);
+
+       /* Parse known bitmap format options */
+       {
+               uint32_t flags = ntohs(header->options);
+
+               if ((flags & BITMAP_OPT_FULL_DAG) == 0)
+                       return error("Unsupported options for bitmap index file "
+                               "(Git requires BITMAP_OPT_FULL_DAG)");
+
+               if (flags & BITMAP_OPT_HASH_CACHE) {
+                       unsigned char *end = index->map + index->map_size - 20;
+                       index->hashes = ((uint32_t *)end) - index->pack->num_objects;
+               }
+       }
+
+       index->entry_count = ntohl(header->entry_count);
+       index->map_pos += sizeof(*header);
+       return 0;
+}
+
+static struct stored_bitmap *store_bitmap(struct bitmap_index *index,
+                                         struct ewah_bitmap *root,
+                                         const unsigned char *sha1,
+                                         struct stored_bitmap *xor_with,
+                                         int flags)
+{
+       struct stored_bitmap *stored;
+       khiter_t hash_pos;
+       int ret;
+
+       stored = xmalloc(sizeof(struct stored_bitmap));
+       stored->root = root;
+       stored->xor = xor_with;
+       stored->flags = flags;
+       hashcpy(stored->sha1, sha1);
+
+       hash_pos = kh_put_sha1(index->bitmaps, stored->sha1, &ret);
+
+       /* a 0 return code means the insertion succeeded with no changes,
+        * because the SHA1 already existed on the map. this is bad, there
+        * shouldn't be duplicated commits in the index */
+       if (ret == 0) {
+               error("Duplicate entry in bitmap index: %s", sha1_to_hex(sha1));
+               return NULL;
+       }
+
+       kh_value(index->bitmaps, hash_pos) = stored;
+       return stored;
+}
+
+static int load_bitmap_entries_v1(struct bitmap_index *index)
+{
+       static const size_t MAX_XOR_OFFSET = 160;
+
+       uint32_t i;
+       struct stored_bitmap **recent_bitmaps;
+       struct bitmap_disk_entry *entry;
+
+       recent_bitmaps = xcalloc(MAX_XOR_OFFSET, sizeof(struct stored_bitmap));
+
+       for (i = 0; i < index->entry_count; ++i) {
+               int xor_offset, flags;
+               struct ewah_bitmap *bitmap = NULL;
+               struct stored_bitmap *xor_bitmap = NULL;
+               uint32_t commit_idx_pos;
+               const unsigned char *sha1;
+
+               entry = (struct bitmap_disk_entry *)(index->map + index->map_pos);
+               index->map_pos += sizeof(struct bitmap_disk_entry);
+
+               commit_idx_pos = ntohl(entry->object_pos);
+               sha1 = nth_packed_object_sha1(index->pack, commit_idx_pos);
+
+               xor_offset = (int)entry->xor_offset;
+               flags = (int)entry->flags;
+
+               bitmap = read_bitmap_1(index);
+               if (!bitmap)
+                       return -1;
+
+               if (xor_offset > MAX_XOR_OFFSET || xor_offset > i)
+                       return error("Corrupted bitmap pack index");
+
+               if (xor_offset > 0) {
+                       xor_bitmap = recent_bitmaps[(i - xor_offset) % MAX_XOR_OFFSET];
+
+                       if (xor_bitmap == NULL)
+                               return error("Invalid XOR offset in bitmap pack index");
+               }
+
+               recent_bitmaps[i % MAX_XOR_OFFSET] = store_bitmap(
+                       index, bitmap, sha1, xor_bitmap, flags);
+       }
+
+       return 0;
+}
+
+static int open_pack_bitmap_1(struct packed_git *packfile)
+{
+       int fd;
+       struct stat st;
+       char *idx_name;
+
+       if (open_pack_index(packfile))
+               return -1;
+
+       idx_name = pack_bitmap_filename(packfile);
+       fd = git_open_noatime(idx_name);
+       free(idx_name);
+
+       if (fd < 0)
+               return -1;
+
+       if (fstat(fd, &st)) {
+               close(fd);
+               return -1;
+       }
+
+       if (bitmap_git.pack) {
+               warning("ignoring extra bitmap file: %s", packfile->pack_name);
+               close(fd);
+               return -1;
+       }
+
+       bitmap_git.pack = packfile;
+       bitmap_git.map_size = xsize_t(st.st_size);
+       bitmap_git.map = xmmap(NULL, bitmap_git.map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       bitmap_git.map_pos = 0;
+       close(fd);
+
+       if (load_bitmap_header(&bitmap_git) < 0) {
+               munmap(bitmap_git.map, bitmap_git.map_size);
+               bitmap_git.map = NULL;
+               bitmap_git.map_size = 0;
+               return -1;
+       }
+
+       return 0;
+}
+
+static int load_pack_bitmap(void)
+{
+       assert(bitmap_git.map && !bitmap_git.loaded);
+
+       bitmap_git.bitmaps = kh_init_sha1();
+       bitmap_git.ext_index.positions = kh_init_sha1_pos();
+       bitmap_git.reverse_index = revindex_for_pack(bitmap_git.pack);
+
+       if (!(bitmap_git.commits = read_bitmap_1(&bitmap_git)) ||
+               !(bitmap_git.trees = read_bitmap_1(&bitmap_git)) ||
+               !(bitmap_git.blobs = read_bitmap_1(&bitmap_git)) ||
+               !(bitmap_git.tags = read_bitmap_1(&bitmap_git)))
+               goto failed;
+
+       if (load_bitmap_entries_v1(&bitmap_git) < 0)
+               goto failed;
+
+       bitmap_git.loaded = 1;
+       return 0;
+
+failed:
+       munmap(bitmap_git.map, bitmap_git.map_size);
+       bitmap_git.map = NULL;
+       bitmap_git.map_size = 0;
+       return -1;
+}
+
+char *pack_bitmap_filename(struct packed_git *p)
+{
+       char *idx_name;
+       int len;
+
+       len = strlen(p->pack_name) - strlen(".pack");
+       idx_name = xmalloc(len + strlen(".bitmap") + 1);
+
+       memcpy(idx_name, p->pack_name, len);
+       memcpy(idx_name + len, ".bitmap", strlen(".bitmap") + 1);
+
+       return idx_name;
+}
+
+static int open_pack_bitmap(void)
+{
+       struct packed_git *p;
+       int ret = -1;
+
+       assert(!bitmap_git.map && !bitmap_git.loaded);
+
+       prepare_packed_git();
+       for (p = packed_git; p; p = p->next) {
+               if (open_pack_bitmap_1(p) == 0)
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+int prepare_bitmap_git(void)
+{
+       if (bitmap_git.loaded)
+               return 0;
+
+       if (!open_pack_bitmap())
+               return load_pack_bitmap();
+
+       return -1;
+}
+
+struct include_data {
+       struct bitmap *base;
+       struct bitmap *seen;
+};
+
+static inline int bitmap_position_extended(const unsigned char *sha1)
+{
+       khash_sha1_pos *positions = bitmap_git.ext_index.positions;
+       khiter_t pos = kh_get_sha1_pos(positions, sha1);
+
+       if (pos < kh_end(positions)) {
+               int bitmap_pos = kh_value(positions, pos);
+               return bitmap_pos + bitmap_git.pack->num_objects;
+       }
+
+       return -1;
+}
+
+static inline int bitmap_position_packfile(const unsigned char *sha1)
+{
+       off_t offset = find_pack_entry_one(sha1, bitmap_git.pack);
+       if (!offset)
+               return -1;
+
+       return find_revindex_position(bitmap_git.reverse_index, offset);
+}
+
+static int bitmap_position(const unsigned char *sha1)
+{
+       int pos = bitmap_position_packfile(sha1);
+       return (pos >= 0) ? pos : bitmap_position_extended(sha1);
+}
+
+static int ext_index_add_object(struct object *object, const char *name)
+{
+       struct eindex *eindex = &bitmap_git.ext_index;
+
+       khiter_t hash_pos;
+       int hash_ret;
+       int bitmap_pos;
+
+       hash_pos = kh_put_sha1_pos(eindex->positions, object->sha1, &hash_ret);
+       if (hash_ret > 0) {
+               if (eindex->count >= eindex->alloc) {
+                       eindex->alloc = (eindex->alloc + 16) * 3 / 2;
+                       eindex->objects = xrealloc(eindex->objects,
+                               eindex->alloc * sizeof(struct object *));
+                       eindex->hashes = xrealloc(eindex->hashes,
+                               eindex->alloc * sizeof(uint32_t));
+               }
+
+               bitmap_pos = eindex->count;
+               eindex->objects[eindex->count] = object;
+               eindex->hashes[eindex->count] = pack_name_hash(name);
+               kh_value(eindex->positions, hash_pos) = bitmap_pos;
+               eindex->count++;
+       } else {
+               bitmap_pos = kh_value(eindex->positions, hash_pos);
+       }
+
+       return bitmap_pos + bitmap_git.pack->num_objects;
+}
+
+static void show_object(struct object *object, const struct name_path *path,
+                       const char *last, void *data)
+{
+       struct bitmap *base = data;
+       int bitmap_pos;
+
+       bitmap_pos = bitmap_position(object->sha1);
+
+       if (bitmap_pos < 0) {
+               char *name = path_name(path, last);
+               bitmap_pos = ext_index_add_object(object, name);
+               free(name);
+       }
+
+       bitmap_set(base, bitmap_pos);
+}
+
+static void show_commit(struct commit *commit, void *data)
+{
+}
+
+static int add_to_include_set(struct include_data *data,
+                             const unsigned char *sha1,
+                             int bitmap_pos)
+{
+       khiter_t hash_pos;
+
+       if (data->seen && bitmap_get(data->seen, bitmap_pos))
+               return 0;
+
+       if (bitmap_get(data->base, bitmap_pos))
+               return 0;
+
+       hash_pos = kh_get_sha1(bitmap_git.bitmaps, sha1);
+       if (hash_pos < kh_end(bitmap_git.bitmaps)) {
+               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, hash_pos);
+               bitmap_or_ewah(data->base, lookup_stored_bitmap(st));
+               return 0;
+       }
+
+       bitmap_set(data->base, bitmap_pos);
+       return 1;
+}
+
+static int should_include(struct commit *commit, void *_data)
+{
+       struct include_data *data = _data;
+       int bitmap_pos;
+
+       bitmap_pos = bitmap_position(commit->object.sha1);
+       if (bitmap_pos < 0)
+               bitmap_pos = ext_index_add_object((struct object *)commit, NULL);
+
+       if (!add_to_include_set(data, commit->object.sha1, bitmap_pos)) {
+               struct commit_list *parent = commit->parents;
+
+               while (parent) {
+                       parent->item->object.flags |= SEEN;
+                       parent = parent->next;
+               }
+
+               return 0;
+       }
+
+       return 1;
+}
+
+static struct bitmap *find_objects(struct rev_info *revs,
+                                  struct object_list *roots,
+                                  struct bitmap *seen)
+{
+       struct bitmap *base = NULL;
+       int needs_walk = 0;
+
+       struct object_list *not_mapped = NULL;
+
+       /*
+        * Go through all the roots for the walk. The ones that have bitmaps
+        * on the bitmap index will be `or`ed together to form an initial
+        * global reachability analysis.
+        *
+        * The ones without bitmaps in the index will be stored in the
+        * `not_mapped_list` for further processing.
+        */
+       while (roots) {
+               struct object *object = roots->item;
+               roots = roots->next;
+
+               if (object->type == OBJ_COMMIT) {
+                       khiter_t pos = kh_get_sha1(bitmap_git.bitmaps, object->sha1);
+
+                       if (pos < kh_end(bitmap_git.bitmaps)) {
+                               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);
+                               struct ewah_bitmap *or_with = lookup_stored_bitmap(st);
+
+                               if (base == NULL)
+                                       base = ewah_to_bitmap(or_with);
+                               else
+                                       bitmap_or_ewah(base, or_with);
+
+                               object->flags |= SEEN;
+                               continue;
+                       }
+               }
+
+               object_list_insert(object, &not_mapped);
+       }
+
+       /*
+        * Best case scenario: We found bitmaps for all the roots,
+        * so the resulting `or` bitmap has the full reachability analysis
+        */
+       if (not_mapped == NULL)
+               return base;
+
+       roots = not_mapped;
+
+       /*
+        * Let's iterate through all the roots that don't have bitmaps to
+        * check if we can determine them to be reachable from the existing
+        * global bitmap.
+        *
+        * If we cannot find them in the existing global bitmap, we'll need
+        * to push them to an actual walk and run it until we can confirm
+        * they are reachable
+        */
+       while (roots) {
+               struct object *object = roots->item;
+               int pos;
+
+               roots = roots->next;
+               pos = bitmap_position(object->sha1);
+
+               if (pos < 0 || base == NULL || !bitmap_get(base, pos)) {
+                       object->flags &= ~UNINTERESTING;
+                       add_pending_object(revs, object, "");
+                       needs_walk = 1;
+               } else {
+                       object->flags |= SEEN;
+               }
+       }
+
+       if (needs_walk) {
+               struct include_data incdata;
+
+               if (base == NULL)
+                       base = bitmap_new();
+
+               incdata.base = base;
+               incdata.seen = seen;
+
+               revs->include_check = should_include;
+               revs->include_check_data = &incdata;
+
+               if (prepare_revision_walk(revs))
+                       die("revision walk setup failed");
+
+               traverse_commit_list(revs, show_commit, show_object, base);
+       }
+
+       return base;
+}
+
+static void show_extended_objects(struct bitmap *objects,
+                                 show_reachable_fn show_reach)
+{
+       struct eindex *eindex = &bitmap_git.ext_index;
+       uint32_t i;
+
+       for (i = 0; i < eindex->count; ++i) {
+               struct object *obj;
+
+               if (!bitmap_get(objects, bitmap_git.pack->num_objects + i))
+                       continue;
+
+               obj = eindex->objects[i];
+               show_reach(obj->sha1, obj->type, 0, eindex->hashes[i], NULL, 0);
+       }
+}
+
+static void show_objects_for_type(
+       struct bitmap *objects,
+       struct ewah_bitmap *type_filter,
+       enum object_type object_type,
+       show_reachable_fn show_reach)
+{
+       size_t pos = 0, i = 0;
+       uint32_t offset;
+
+       struct ewah_iterator it;
+       eword_t filter;
+
+       if (bitmap_git.reuse_objects == bitmap_git.pack->num_objects)
+               return;
+
+       ewah_iterator_init(&it, type_filter);
+
+       while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {
+               eword_t word = objects->words[i] & filter;
+
+               for (offset = 0; offset < BITS_IN_WORD; ++offset) {
+                       const unsigned char *sha1;
+                       struct revindex_entry *entry;
+                       uint32_t hash = 0;
+
+                       if ((word >> offset) == 0)
+                               break;
+
+                       offset += ewah_bit_ctz64(word >> offset);
+
+                       if (pos + offset < bitmap_git.reuse_objects)
+                               continue;
+
+                       entry = &bitmap_git.reverse_index->revindex[pos + offset];
+                       sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);
+
+                       if (bitmap_git.hashes)
+                               hash = ntohl(bitmap_git.hashes[entry->nr]);
+
+                       show_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset);
+               }
+
+               pos += BITS_IN_WORD;
+               i++;
+       }
+}
+
+static int in_bitmapped_pack(struct object_list *roots)
+{
+       while (roots) {
+               struct object *object = roots->item;
+               roots = roots->next;
+
+               if (find_pack_entry_one(object->sha1, bitmap_git.pack) > 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+int prepare_bitmap_walk(struct rev_info *revs)
+{
+       unsigned int i;
+       unsigned int pending_nr = revs->pending.nr;
+       struct object_array_entry *pending_e = revs->pending.objects;
+
+       struct object_list *wants = NULL;
+       struct object_list *haves = NULL;
+
+       struct bitmap *wants_bitmap = NULL;
+       struct bitmap *haves_bitmap = NULL;
+
+       if (!bitmap_git.loaded) {
+               /* try to open a bitmapped pack, but don't parse it yet
+                * because we may not need to use it */
+               if (open_pack_bitmap() < 0)
+                       return -1;
+       }
+
+       for (i = 0; i < pending_nr; ++i) {
+               struct object *object = pending_e[i].item;
+
+               if (object->type == OBJ_NONE)
+                       parse_object_or_die(object->sha1, NULL);
+
+               while (object->type == OBJ_TAG) {
+                       struct tag *tag = (struct tag *) object;
+
+                       if (object->flags & UNINTERESTING)
+                               object_list_insert(object, &haves);
+                       else
+                               object_list_insert(object, &wants);
+
+                       if (!tag->tagged)
+                               die("bad tag");
+                       object = parse_object_or_die(tag->tagged->sha1, NULL);
+               }
+
+               if (object->flags & UNINTERESTING)
+                       object_list_insert(object, &haves);
+               else
+                       object_list_insert(object, &wants);
+       }
+
+       /*
+        * if we have a HAVES list, but none of those haves is contained
+        * in the packfile that has a bitmap, we don't have anything to
+        * optimize here
+        */
+       if (haves && !in_bitmapped_pack(haves))
+               return -1;
+
+       /* if we don't want anything, we're done here */
+       if (!wants)
+               return -1;
+
+       /*
+        * now we're going to use bitmaps, so load the actual bitmap entries
+        * from disk. this is the point of no return; after this the rev_list
+        * becomes invalidated and we must perform the revwalk through bitmaps
+        */
+       if (!bitmap_git.loaded && load_pack_bitmap() < 0)
+               return -1;
+
+       revs->pending.nr = 0;
+       revs->pending.alloc = 0;
+       revs->pending.objects = NULL;
+
+       if (haves) {
+               haves_bitmap = find_objects(revs, haves, NULL);
+               reset_revision_walk();
+
+               if (haves_bitmap == NULL)
+                       die("BUG: failed to perform bitmap walk");
+       }
+
+       wants_bitmap = find_objects(revs, wants, haves_bitmap);
+
+       if (!wants_bitmap)
+               die("BUG: failed to perform bitmap walk");
+
+       if (haves_bitmap)
+               bitmap_and_not(wants_bitmap, haves_bitmap);
+
+       bitmap_git.result = wants_bitmap;
+
+       bitmap_free(haves_bitmap);
+       return 0;
+}
+
+int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
+                                      uint32_t *entries,
+                                      off_t *up_to)
+{
+       /*
+        * Reuse the packfile content if we need more than
+        * 90% of its objects
+        */
+       static const double REUSE_PERCENT = 0.9;
+
+       struct bitmap *result = bitmap_git.result;
+       uint32_t reuse_threshold;
+       uint32_t i, reuse_objects = 0;
+
+       assert(result);
+
+       for (i = 0; i < result->word_alloc; ++i) {
+               if (result->words[i] != (eword_t)~0) {
+                       reuse_objects += ewah_bit_ctz64(~result->words[i]);
+                       break;
+               }
+
+               reuse_objects += BITS_IN_WORD;
+       }
+
+#ifdef GIT_BITMAP_DEBUG
+       {
+               const unsigned char *sha1;
+               struct revindex_entry *entry;
+
+               entry = &bitmap_git.reverse_index->revindex[reuse_objects];
+               sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);
+
+               fprintf(stderr, "Failed to reuse at %d (%016llx)\n",
+                       reuse_objects, result->words[i]);
+               fprintf(stderr, " %s\n", sha1_to_hex(sha1));
+       }
+#endif
+
+       if (!reuse_objects)
+               return -1;
+
+       if (reuse_objects >= bitmap_git.pack->num_objects) {
+               bitmap_git.reuse_objects = *entries = bitmap_git.pack->num_objects;
+               *up_to = -1; /* reuse the full pack */
+               *packfile = bitmap_git.pack;
+               return 0;
+       }
+
+       reuse_threshold = bitmap_popcount(bitmap_git.result) * REUSE_PERCENT;
+
+       if (reuse_objects < reuse_threshold)
+               return -1;
+
+       bitmap_git.reuse_objects = *entries = reuse_objects;
+       *up_to = bitmap_git.reverse_index->revindex[reuse_objects].offset;
+       *packfile = bitmap_git.pack;
+
+       return 0;
+}
+
+void traverse_bitmap_commit_list(show_reachable_fn show_reachable)
+{
+       assert(bitmap_git.result);
+
+       show_objects_for_type(bitmap_git.result, bitmap_git.commits,
+               OBJ_COMMIT, show_reachable);
+       show_objects_for_type(bitmap_git.result, bitmap_git.trees,
+               OBJ_TREE, show_reachable);
+       show_objects_for_type(bitmap_git.result, bitmap_git.blobs,
+               OBJ_BLOB, show_reachable);
+       show_objects_for_type(bitmap_git.result, bitmap_git.tags,
+               OBJ_TAG, show_reachable);
+
+       show_extended_objects(bitmap_git.result, show_reachable);
+
+       bitmap_free(bitmap_git.result);
+       bitmap_git.result = NULL;
+}
+
+static uint32_t count_object_type(struct bitmap *objects,
+                                 enum object_type type)
+{
+       struct eindex *eindex = &bitmap_git.ext_index;
+
+       uint32_t i = 0, count = 0;
+       struct ewah_iterator it;
+       eword_t filter;
+
+       switch (type) {
+       case OBJ_COMMIT:
+               ewah_iterator_init(&it, bitmap_git.commits);
+               break;
+
+       case OBJ_TREE:
+               ewah_iterator_init(&it, bitmap_git.trees);
+               break;
+
+       case OBJ_BLOB:
+               ewah_iterator_init(&it, bitmap_git.blobs);
+               break;
+
+       case OBJ_TAG:
+               ewah_iterator_init(&it, bitmap_git.tags);
+               break;
+
+       default:
+               return 0;
+       }
+
+       while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {
+               eword_t word = objects->words[i++] & filter;
+               count += ewah_bit_popcount64(word);
+       }
+
+       for (i = 0; i < eindex->count; ++i) {
+               if (eindex->objects[i]->type == type &&
+                       bitmap_get(objects, bitmap_git.pack->num_objects + i))
+                       count++;
+       }
+
+       return count;
+}
+
+void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees,
+                             uint32_t *blobs, uint32_t *tags)
+{
+       assert(bitmap_git.result);
+
+       if (commits)
+               *commits = count_object_type(bitmap_git.result, OBJ_COMMIT);
+
+       if (trees)
+               *trees = count_object_type(bitmap_git.result, OBJ_TREE);
+
+       if (blobs)
+               *blobs = count_object_type(bitmap_git.result, OBJ_BLOB);
+
+       if (tags)
+               *tags = count_object_type(bitmap_git.result, OBJ_TAG);
+}
+
+struct bitmap_test_data {
+       struct bitmap *base;
+       struct progress *prg;
+       size_t seen;
+};
+
+static void test_show_object(struct object *object,
+                            const struct name_path *path,
+                            const char *last, void *data)
+{
+       struct bitmap_test_data *tdata = data;
+       int bitmap_pos;
+
+       bitmap_pos = bitmap_position(object->sha1);
+       if (bitmap_pos < 0)
+               die("Object not in bitmap: %s\n", sha1_to_hex(object->sha1));
+
+       bitmap_set(tdata->base, bitmap_pos);
+       display_progress(tdata->prg, ++tdata->seen);
+}
+
+static void test_show_commit(struct commit *commit, void *data)
+{
+       struct bitmap_test_data *tdata = data;
+       int bitmap_pos;
+
+       bitmap_pos = bitmap_position(commit->object.sha1);
+       if (bitmap_pos < 0)
+               die("Object not in bitmap: %s\n", sha1_to_hex(commit->object.sha1));
+
+       bitmap_set(tdata->base, bitmap_pos);
+       display_progress(tdata->prg, ++tdata->seen);
+}
+
+void test_bitmap_walk(struct rev_info *revs)
+{
+       struct object *root;
+       struct bitmap *result = NULL;
+       khiter_t pos;
+       size_t result_popcnt;
+       struct bitmap_test_data tdata;
+
+       if (prepare_bitmap_git())
+               die("failed to load bitmap indexes");
+
+       if (revs->pending.nr != 1)
+               die("you must specify exactly one commit to test");
+
+       fprintf(stderr, "Bitmap v%d test (%d entries loaded)\n",
+               bitmap_git.version, bitmap_git.entry_count);
+
+       root = revs->pending.objects[0].item;
+       pos = kh_get_sha1(bitmap_git.bitmaps, root->sha1);
+
+       if (pos < kh_end(bitmap_git.bitmaps)) {
+               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);
+               struct ewah_bitmap *bm = lookup_stored_bitmap(st);
+
+               fprintf(stderr, "Found bitmap for %s. %d bits / %08x checksum\n",
+                       sha1_to_hex(root->sha1), (int)bm->bit_size, ewah_checksum(bm));
+
+               result = ewah_to_bitmap(bm);
+       }
+
+       if (result == NULL)
+               die("Commit %s doesn't have an indexed bitmap", sha1_to_hex(root->sha1));
+
+       revs->tag_objects = 1;
+       revs->tree_objects = 1;
+       revs->blob_objects = 1;
+
+       result_popcnt = bitmap_popcount(result);
+
+       if (prepare_revision_walk(revs))
+               die("revision walk setup failed");
+
+       tdata.base = bitmap_new();
+       tdata.prg = start_progress("Verifying bitmap entries", result_popcnt);
+       tdata.seen = 0;
+
+       traverse_commit_list(revs, &test_show_commit, &test_show_object, &tdata);
+
+       stop_progress(&tdata.prg);
+
+       if (bitmap_equals(result, tdata.base))
+               fprintf(stderr, "OK!\n");
+       else
+               fprintf(stderr, "Mismatch!\n");
+}
+
+static int rebuild_bitmap(uint32_t *reposition,
+                         struct ewah_bitmap *source,
+                         struct bitmap *dest)
+{
+       uint32_t pos = 0;
+       struct ewah_iterator it;
+       eword_t word;
+
+       ewah_iterator_init(&it, source);
+
+       while (ewah_iterator_next(&word, &it)) {
+               uint32_t offset, bit_pos;
+
+               for (offset = 0; offset < BITS_IN_WORD; ++offset) {
+                       if ((word >> offset) == 0)
+                               break;
+
+                       offset += ewah_bit_ctz64(word >> offset);
+
+                       bit_pos = reposition[pos + offset];
+                       if (bit_pos > 0)
+                               bitmap_set(dest, bit_pos - 1);
+                       else /* can't reuse, we don't have the object */
+                               return -1;
+               }
+
+               pos += BITS_IN_WORD;
+       }
+       return 0;
+}
+
+int rebuild_existing_bitmaps(struct packing_data *mapping,
+                            khash_sha1 *reused_bitmaps,
+                            int show_progress)
+{
+       uint32_t i, num_objects;
+       uint32_t *reposition;
+       struct bitmap *rebuild;
+       struct stored_bitmap *stored;
+       struct progress *progress = NULL;
+
+       khiter_t hash_pos;
+       int hash_ret;
+
+       if (prepare_bitmap_git() < 0)
+               return -1;
+
+       num_objects = bitmap_git.pack->num_objects;
+       reposition = xcalloc(num_objects, sizeof(uint32_t));
+
+       for (i = 0; i < num_objects; ++i) {
+               const unsigned char *sha1;
+               struct revindex_entry *entry;
+               struct object_entry *oe;
+
+               entry = &bitmap_git.reverse_index->revindex[i];
+               sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);
+               oe = packlist_find(mapping, sha1, NULL);
+
+               if (oe)
+                       reposition[i] = oe->in_pack_pos + 1;
+       }
+
+       rebuild = bitmap_new();
+       i = 0;
+
+       if (show_progress)
+               progress = start_progress("Reusing bitmaps", 0);
+
+       kh_foreach_value(bitmap_git.bitmaps, stored, {
+               if (stored->flags & BITMAP_FLAG_REUSE) {
+                       if (!rebuild_bitmap(reposition,
+                                           lookup_stored_bitmap(stored),
+                                           rebuild)) {
+                               hash_pos = kh_put_sha1(reused_bitmaps,
+                                                      stored->sha1,
+                                                      &hash_ret);
+                               kh_value(reused_bitmaps, hash_pos) =
+                                       bitmap_to_ewah(rebuild);
+                       }
+                       bitmap_reset(rebuild);
+                       display_progress(progress, ++i);
+               }
+       });
+
+       stop_progress(&progress);
+
+       free(reposition);
+       bitmap_free(rebuild);
+       return 0;
+}
diff --git a/pack-bitmap.h b/pack-bitmap.h
new file mode 100644 (file)
index 0000000..8b7f4e9
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef PACK_BITMAP_H
+#define PACK_BITMAP_H
+
+#include "ewah/ewok.h"
+#include "khash.h"
+#include "pack-objects.h"
+
+struct bitmap_disk_entry {
+       uint32_t object_pos;
+       uint8_t xor_offset;
+       uint8_t flags;
+} __attribute__((packed));
+
+struct bitmap_disk_header {
+       char magic[4];
+       uint16_t version;
+       uint16_t options;
+       uint32_t entry_count;
+       unsigned char checksum[20];
+};
+
+static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};
+
+#define NEEDS_BITMAP (1u<<22)
+
+enum pack_bitmap_opts {
+       BITMAP_OPT_FULL_DAG = 1,
+       BITMAP_OPT_HASH_CACHE = 4,
+};
+
+enum pack_bitmap_flags {
+       BITMAP_FLAG_REUSE = 0x1
+};
+
+typedef int (*show_reachable_fn)(
+       const unsigned char *sha1,
+       enum object_type type,
+       int flags,
+       uint32_t hash,
+       struct packed_git *found_pack,
+       off_t found_offset);
+
+int prepare_bitmap_git(void);
+void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees, uint32_t *blobs, uint32_t *tags);
+void traverse_bitmap_commit_list(show_reachable_fn show_reachable);
+void test_bitmap_walk(struct rev_info *revs);
+char *pack_bitmap_filename(struct packed_git *p);
+int prepare_bitmap_walk(struct rev_info *revs);
+int reuse_partial_packfile_from_bitmap(struct packed_git **packfile, uint32_t *entries, off_t *up_to);
+int rebuild_existing_bitmaps(struct packing_data *mapping, khash_sha1 *reused_bitmaps, int show_progress);
+
+void bitmap_writer_show_progress(int show);
+void bitmap_writer_set_checksum(unsigned char *sha1);
+void bitmap_writer_build_type_index(struct pack_idx_entry **index, uint32_t index_nr);
+void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack);
+void bitmap_writer_select_commits(struct commit **indexed_commits,
+               unsigned int indexed_commits_nr, int max_bitmaps);
+void bitmap_writer_build(struct packing_data *to_pack);
+void bitmap_writer_finish(struct pack_idx_entry **index,
+                         uint32_t index_nr,
+                         const char *filename,
+                         uint16_t options);
+
+#endif
diff --git a/pack-objects.c b/pack-objects.c
new file mode 100644 (file)
index 0000000..d01d851
--- /dev/null
@@ -0,0 +1,111 @@
+#include "cache.h"
+#include "object.h"
+#include "pack.h"
+#include "pack-objects.h"
+
+static uint32_t locate_object_entry_hash(struct packing_data *pdata,
+                                        const unsigned char *sha1,
+                                        int *found)
+{
+       uint32_t i, hash, mask = (pdata->index_size - 1);
+
+       memcpy(&hash, sha1, sizeof(uint32_t));
+       i = hash & mask;
+
+       while (pdata->index[i] > 0) {
+               uint32_t pos = pdata->index[i] - 1;
+
+               if (!hashcmp(sha1, pdata->objects[pos].idx.sha1)) {
+                       *found = 1;
+                       return i;
+               }
+
+               i = (i + 1) & mask;
+       }
+
+       *found = 0;
+       return i;
+}
+
+static inline uint32_t closest_pow2(uint32_t v)
+{
+       v = v - 1;
+       v |= v >> 1;
+       v |= v >> 2;
+       v |= v >> 4;
+       v |= v >> 8;
+       v |= v >> 16;
+       return v + 1;
+}
+
+static void rehash_objects(struct packing_data *pdata)
+{
+       uint32_t i;
+       struct object_entry *entry;
+
+       pdata->index_size = closest_pow2(pdata->nr_objects * 3);
+       if (pdata->index_size < 1024)
+               pdata->index_size = 1024;
+
+       pdata->index = xrealloc(pdata->index, sizeof(uint32_t) * pdata->index_size);
+       memset(pdata->index, 0, sizeof(int) * pdata->index_size);
+
+       entry = pdata->objects;
+
+       for (i = 0; i < pdata->nr_objects; i++) {
+               int found;
+               uint32_t ix = locate_object_entry_hash(pdata, entry->idx.sha1, &found);
+
+               if (found)
+                       die("BUG: Duplicate object in hash");
+
+               pdata->index[ix] = i + 1;
+               entry++;
+       }
+}
+
+struct object_entry *packlist_find(struct packing_data *pdata,
+                                  const unsigned char *sha1,
+                                  uint32_t *index_pos)
+{
+       uint32_t i;
+       int found;
+
+       if (!pdata->index_size)
+               return NULL;
+
+       i = locate_object_entry_hash(pdata, sha1, &found);
+
+       if (index_pos)
+               *index_pos = i;
+
+       if (!found)
+               return NULL;
+
+       return &pdata->objects[pdata->index[i] - 1];
+}
+
+struct object_entry *packlist_alloc(struct packing_data *pdata,
+                                   const unsigned char *sha1,
+                                   uint32_t index_pos)
+{
+       struct object_entry *new_entry;
+
+       if (pdata->nr_objects >= pdata->nr_alloc) {
+               pdata->nr_alloc = (pdata->nr_alloc  + 1024) * 3 / 2;
+               pdata->objects = xrealloc(pdata->objects,
+                                         pdata->nr_alloc * sizeof(*new_entry));
+       }
+
+       new_entry = pdata->objects + pdata->nr_objects++;
+
+       memset(new_entry, 0, sizeof(*new_entry));
+       hashcpy(new_entry->idx.sha1, sha1);
+
+       if (pdata->index_size * 3 <= pdata->nr_objects * 4)
+               rehash_objects(pdata);
+       else
+               pdata->index[index_pos] = pdata->nr_objects;
+
+       return new_entry;
+}
diff --git a/pack-objects.h b/pack-objects.h
new file mode 100644 (file)
index 0000000..d1b98b3
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef PACK_OBJECTS_H
+#define PACK_OBJECTS_H
+
+struct object_entry {
+       struct pack_idx_entry idx;
+       unsigned long size;     /* uncompressed size */
+       struct packed_git *in_pack;     /* already in pack */
+       off_t in_pack_offset;
+       struct object_entry *delta;     /* delta base object */
+       struct object_entry *delta_child; /* deltified objects who bases me */
+       struct object_entry *delta_sibling; /* other deltified objects who
+                                            * uses the same base as me
+                                            */
+       void *delta_data;       /* cached delta (uncompressed) */
+       unsigned long delta_size;       /* delta data size (uncompressed) */
+       unsigned long z_delta_size;     /* delta data size (compressed) */
+       enum object_type type;
+       enum object_type in_pack_type;  /* could be delta */
+       uint32_t hash;                  /* name hint hash */
+       unsigned int in_pack_pos;
+       unsigned char in_pack_header_size;
+       unsigned preferred_base:1; /*
+                                   * we do not pack this, but is available
+                                   * to be used as the base object to delta
+                                   * objects against.
+                                   */
+       unsigned no_try_delta:1;
+       unsigned tagged:1; /* near the very tip of refs */
+       unsigned filled:1; /* assigned write-order */
+};
+
+struct packing_data {
+       struct object_entry *objects;
+       uint32_t nr_objects, nr_alloc;
+
+       int32_t *index;
+       uint32_t index_size;
+};
+
+struct object_entry *packlist_alloc(struct packing_data *pdata,
+                                   const unsigned char *sha1,
+                                   uint32_t index_pos);
+
+struct object_entry *packlist_find(struct packing_data *pdata,
+                                  const unsigned char *sha1,
+                                  uint32_t *index_pos);
+
+static inline uint32_t pack_name_hash(const char *name)
+{
+       uint32_t c, hash = 0;
+
+       if (!name)
+               return 0;
+
+       /*
+        * This effectively just creates a sortable number from the
+        * last sixteen non-whitespace characters. Last characters
+        * count "most", so things that end in ".c" sort together.
+        */
+       while ((c = *name++) != 0) {
+               if (isspace(c))
+                       continue;
+               hash = (hash >> 2) + (c << 24);
+       }
+       return hash;
+}
+
+#endif
index b4d2b35bb37120f97ec000524d12a85d91949119..5bd7c619803c8457a8c1a1a69a11758d34b49eac 100644 (file)
  * get the object sha1 from the main index.
  */
 
-struct pack_revindex {
-       struct packed_git *p;
-       struct revindex_entry *revindex;
-};
-
 static struct pack_revindex *pack_revindex;
 static int pack_revindex_hashsz;
 
@@ -201,15 +196,14 @@ static void create_pack_revindex(struct pack_revindex *rix)
        sort_revindex(rix->revindex, num_ent, p->pack_size);
 }
 
-struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
+struct pack_revindex *revindex_for_pack(struct packed_git *p)
 {
        int num;
-       unsigned lo, hi;
        struct pack_revindex *rix;
-       struct revindex_entry *revindex;
 
        if (!pack_revindex_hashsz)
                init_pack_revindex();
+
        num = pack_revindex_ix(p);
        if (num < 0)
                die("internal error: pack revindex fubar");
@@ -217,30 +211,37 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
        rix = &pack_revindex[num];
        if (!rix->revindex)
                create_pack_revindex(rix);
-       revindex = rix->revindex;
 
-       lo = 0;
-       hi = p->num_objects + 1;
+       return rix;
+}
+
+int find_revindex_position(struct pack_revindex *pridx, off_t ofs)
+{
+       int lo = 0;
+       int hi = pridx->p->num_objects + 1;
+       struct revindex_entry *revindex = pridx->revindex;
+
        do {
                unsigned mi = lo + (hi - lo) / 2;
                if (revindex[mi].offset == ofs) {
-                       return revindex + mi;
+                       return mi;
                } else if (ofs < revindex[mi].offset)
                        hi = mi;
                else
                        lo = mi + 1;
        } while (lo < hi);
+
        error("bad offset for revindex");
-       return NULL;
+       return -1;
 }
 
-void discard_revindex(void)
+struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
 {
-       if (pack_revindex_hashsz) {
-               int i;
-               for (i = 0; i < pack_revindex_hashsz; i++)
-                       free(pack_revindex[i].revindex);
-               free(pack_revindex);
-               pack_revindex_hashsz = 0;
-       }
+       struct pack_revindex *pridx = revindex_for_pack(p);
+       int pos = find_revindex_position(pridx, ofs);
+
+       if (pos < 0)
+               return NULL;
+
+       return pridx->revindex + pos;
 }
index 8d5027ad917224f689e786e9a0b4e9a387e59dfe..d737f989659da7f58d7df153de9a2ac30b162108 100644 (file)
@@ -6,7 +6,14 @@ struct revindex_entry {
        unsigned int nr;
 };
 
+struct pack_revindex {
+       struct packed_git *p;
+       struct revindex_entry *revindex;
+};
+
+struct pack_revindex *revindex_for_pack(struct packed_git *p);
+int find_revindex_position(struct pack_revindex *pridx, off_t ofs);
+
 struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);
-void discard_revindex(void);
 
 #endif
index 605d01b25cbd9d796810f8aa810894a2c198da28..9b8308b7594263c4900a6d75c18b91cea959fd70 100644 (file)
@@ -364,5 +364,7 @@ void finish_tmp_packfile(char *name_buffer,
        if (rename(idx_tmp_name, name_buffer))
                die_errno("unable to rename temporary index file");
 
+       *end_of_name_prefix = '\0';
+
        free((void *)idx_tmp_name);
 }
diff --git a/path.c b/path.c
index 24594c41120bebab1e04f5bc4bd8fc484ea71b97..f9c5062427e7d8170a1e2e597fcf22ae517a865e 100644 (file)
--- a/path.c
+++ b/path.c
@@ -265,12 +265,12 @@ static struct passwd *getpw_str(const char *username, size_t len)
 char *expand_user_path(const char *path)
 {
        struct strbuf user_path = STRBUF_INIT;
-       const char *first_slash = strchrnul(path, '/');
        const char *to_copy = path;
 
        if (path == NULL)
                goto return_null;
        if (path[0] == '~') {
+               const char *first_slash = strchrnul(path, '/');
                const char *username = path + 1;
                size_t username_len = first_slash - username;
                if (username_len == 0) {
index 6cb9fd3014a424d11647e48de3b841012ccc2ffe..80430999553d8fda8d095b007bb08c1999bf6993 100644 (file)
@@ -33,7 +33,7 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec,
                return;
        for (i = 0; i < active_nr; i++) {
                const struct cache_entry *ce = active_cache[i];
-               match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen);
+               ce_path_match(ce, pathspec, seen);
        }
 }
 
index e10263f2b2ce90f08ed082022ef16d13f1c7770a..0b9d59e8fa43dca6b580d9436712a7d09f27472b 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -1075,7 +1075,7 @@ msgstr "Votre branche est basée sur '%s', mais la branche amont a disparu.\n"
 
 #: remote.c:1875
 msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (utilisez \"git branch -unset-upstream\" pour corriger)\n"
+msgstr "  (utilisez \"git branch --unset-upstream\" pour corriger)\n"
 
 #: remote.c:1878
 #, c-format
@@ -3266,7 +3266,7 @@ msgstr "    git branch -d %s\n"
 #: builtin/branch.c:1027
 #, c-format
 msgid "    git branch --set-upstream-to %s\n"
-msgstr "    git branch -set-upstream-to %s\n"
+msgstr "    git branch --set-upstream-to %s\n"
 
 #: builtin/bundle.c:47
 #, c-format
index 8c44ceb2c715936b314fc10a7aae3dfb44172327..968ee25eae13f82fe4f93254d77660456732a5c6 100644 (file)
@@ -3,6 +3,7 @@
  */
 #include "cache.h"
 #include "pathspec.h"
+#include "dir.h"
 
 #ifdef NO_PTHREADS
 static void preload_index(struct index_state *index,
@@ -53,7 +54,7 @@ static void *preload_thread(void *_data)
                        continue;
                if (ce_uptodate(ce))
                        continue;
-               if (!ce_path_match(ce, &p->pathspec))
+               if (!ce_path_match(ce, &p->pathspec, NULL))
                        continue;
                if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
                        continue;
index 33dd676ccbbd24e0bace49347f1f5d81b5099bcc..fb440b4d9d1aa002ebc760e4fa4e0f0d028fd868 100644 (file)
@@ -15,7 +15,8 @@
 #include "strbuf.h"
 #include "varint.h"
 
-static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
+static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
+                                              unsigned int options);
 
 /* Mask for the name length in ce_flags in the on-disk index */
 
@@ -47,6 +48,7 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache
        struct cache_entry *old = istate->cache[nr];
 
        remove_name_hash(istate, old);
+       free(old);
        set_index_entry(istate, nr, ce);
        istate->cache_changed = 1;
 }
@@ -58,7 +60,7 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
 
        new = xmalloc(cache_entry_size(namelen));
        copy_cache_entry(new, old);
-       new->ce_flags &= ~CE_STATE_MASK;
+       new->ce_flags &= ~CE_HASHED;
        new->ce_namelen = namelen;
        memcpy(new->name, new_name, namelen + 1);
 
@@ -478,6 +480,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 
        record_resolve_undo(istate, ce);
        remove_name_hash(istate, ce);
+       free(ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
@@ -499,8 +502,10 @@ void remove_marked_cache_entries(struct index_state *istate)
        unsigned int i, j;
 
        for (i = j = 0; i < istate->cache_nr; i++) {
-               if (ce_array[i]->ce_flags & CE_REMOVE)
+               if (ce_array[i]->ce_flags & CE_REMOVE) {
                        remove_name_hash(istate, ce_array[i]);
+                       free(ce_array[i]);
+               }
                else
                        ce_array[j++] = ce_array[i];
        }
@@ -579,7 +584,7 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_
        return new;
 }
 
-static void record_intent_to_add(struct cache_entry *ce)
+void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
 {
        unsigned char sha1[20];
        if (write_sha1_file("", 0, blob_type, sha1))
@@ -665,7 +670,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                if (index_path(ce->sha1, path, st, HASH_WRITE_OBJECT))
                        return error("unable to index file %s", path);
        } else
-               record_intent_to_add(ce);
+               set_object_name_for_intent_to_add_entry(ce);
 
        if (ignore_case && alias && different_name(ce, alias))
                ce = create_alias_ce(ce, alias);
@@ -696,7 +701,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int flags)
 
 struct cache_entry *make_cache_entry(unsigned int mode,
                const unsigned char *sha1, const char *path, int stage,
-               int refresh)
+               unsigned int refresh_options)
 {
        int size, len;
        struct cache_entry *ce;
@@ -716,10 +721,7 @@ struct cache_entry *make_cache_entry(unsigned int mode,
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
 
-       if (refresh)
-               return refresh_cache_entry(ce, 0);
-
-       return ce;
+       return refresh_cache_entry(ce, refresh_options);
 }
 
 int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
@@ -728,11 +730,6 @@ int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
        return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
 }
 
-int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec)
-{
-       return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL);
-}
-
 /*
  * We fundamentally don't like some paths: we don't want
  * dot or dot-dot anywhere, and for obvious reasons don't
@@ -1029,10 +1026,12 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
        struct stat st;
        struct cache_entry *updated;
        int changed, size;
+       int refresh = options & CE_MATCH_REFRESH;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
        int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
+       int ignore_missing = options & CE_MATCH_IGNORE_MISSING;
 
-       if (ce_uptodate(ce))
+       if (!refresh || ce_uptodate(ce))
                return ce;
 
        /*
@@ -1050,6 +1049,8 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
        }
 
        if (lstat(ce->name, &st) < 0) {
+               if (ignore_missing && errno == ENOENT)
+                       return ce;
                if (err)
                        *err = errno;
                return NULL;
@@ -1127,7 +1128,9 @@ int refresh_index(struct index_state *istate, unsigned int flags,
        int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
        int first = 1;
        int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
-       unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
+       unsigned int options = (CE_MATCH_REFRESH |
+                               (really ? CE_MATCH_IGNORE_VALID : 0) |
+                               (not_new ? CE_MATCH_IGNORE_MISSING : 0));
        const char *modified_fmt;
        const char *deleted_fmt;
        const char *typechange_fmt;
@@ -1149,8 +1152,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
                        continue;
 
-               if (pathspec &&
-                   !match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen))
+               if (pathspec && !ce_path_match(ce, pathspec, seen))
                        filtered = 1;
 
                if (ce_stage(ce)) {
@@ -1176,8 +1178,6 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                if (!new) {
                        const char *fmt;
 
-                       if (not_new && cache_errno == ENOENT)
-                               continue;
                        if (really && cache_errno == EINVAL) {
                                /* If we are doing --really-refresh that
                                 * means the index is not valid anymore.
@@ -1207,9 +1207,10 @@ int refresh_index(struct index_state *istate, unsigned int flags,
        return has_errors;
 }
 
-static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
+static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
+                                              unsigned int options)
 {
-       return refresh_cache_ent(&the_index, ce, really, NULL, NULL);
+       return refresh_cache_ent(&the_index, ce, options, NULL, NULL);
 }
 
 
@@ -1313,26 +1314,6 @@ int read_index(struct index_state *istate)
        return read_index_from(istate, get_index_file());
 }
 
-#ifndef NEEDS_ALIGNED_ACCESS
-#define ntoh_s(var) ntohs(var)
-#define ntoh_l(var) ntohl(var)
-#else
-static inline uint16_t ntoh_s_force_align(void *p)
-{
-       uint16_t x;
-       memcpy(&x, p, sizeof(x));
-       return ntohs(x);
-}
-static inline uint32_t ntoh_l_force_align(void *p)
-{
-       uint32_t x;
-       memcpy(&x, p, sizeof(x));
-       return ntohl(x);
-}
-#define ntoh_s(var) ntoh_s_force_align(&(var))
-#define ntoh_l(var) ntoh_l_force_align(&(var))
-#endif
-
 static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *ondisk,
                                                   unsigned int flags,
                                                   const char *name,
@@ -1340,16 +1321,16 @@ static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *on
 {
        struct cache_entry *ce = xmalloc(cache_entry_size(len));
 
-       ce->ce_stat_data.sd_ctime.sec = ntoh_l(ondisk->ctime.sec);
-       ce->ce_stat_data.sd_mtime.sec = ntoh_l(ondisk->mtime.sec);
-       ce->ce_stat_data.sd_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
-       ce->ce_stat_data.sd_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
-       ce->ce_stat_data.sd_dev   = ntoh_l(ondisk->dev);
-       ce->ce_stat_data.sd_ino   = ntoh_l(ondisk->ino);
-       ce->ce_mode  = ntoh_l(ondisk->mode);
-       ce->ce_stat_data.sd_uid   = ntoh_l(ondisk->uid);
-       ce->ce_stat_data.sd_gid   = ntoh_l(ondisk->gid);
-       ce->ce_stat_data.sd_size  = ntoh_l(ondisk->size);
+       ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
+       ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
+       ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
+       ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
+       ce->ce_stat_data.sd_dev   = get_be32(&ondisk->dev);
+       ce->ce_stat_data.sd_ino   = get_be32(&ondisk->ino);
+       ce->ce_mode  = get_be32(&ondisk->mode);
+       ce->ce_stat_data.sd_uid   = get_be32(&ondisk->uid);
+       ce->ce_stat_data.sd_gid   = get_be32(&ondisk->gid);
+       ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        hashcpy(ce->sha1, ondisk->sha1);
@@ -1389,14 +1370,14 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk,
        unsigned int flags;
 
        /* On-disk flags are just 16 bits */
-       flags = ntoh_s(ondisk->flags);
+       flags = get_be16(&ondisk->flags);
        len = flags & CE_NAMEMASK;
 
        if (flags & CE_EXTENDED) {
                struct ondisk_cache_entry_extended *ondisk2;
                int extended_flags;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
-               extended_flags = ntoh_s(ondisk2->flags2) << 16;
+               extended_flags = get_be16(&ondisk2->flags2) << 16;
                /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
                if (extended_flags & ~CE_EXTENDED_FLAGS)
                        die("Unknown index entry format %08x", extended_flags);
@@ -1894,7 +1875,7 @@ int read_index_unmerged(struct index_state *istate)
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",
-                                    ce->name);
+                                    new_ce->name);
                i = index_name_pos(istate, new_ce->name, len);
        }
        return unmerged;
index 10cb0114eafdfd9760b3fdb3c0217d801195ba13..52c2d96ce6183ca8b3498794304fa8dc98f8f42a 100644 (file)
@@ -423,11 +423,8 @@ static int run_slot(struct active_request_slot *slot,
        if (!results)
                results = &results_buf;
 
-       slot->results = results;
-       slot->curl_result = curl_easy_perform(slot->curl);
-       finish_active_slot(slot);
+       err = run_one_slot(slot, results);
 
-       err = handle_curl_result(results);
        if (err != HTTP_OK && err != HTTP_REAUTH) {
                error("RPC failed; result=%d, HTTP code = %ld",
                      results->curl_result, results->http_code);
index 1f2d21a72f56a68a7f9307bfd9092087687cffae..d55aa8a01b4a4f200f24972795f2887644a06d43 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -672,8 +672,8 @@ int rerere_forget(struct pathspec *pathspec)
        find_conflict(&conflict);
        for (i = 0; i < conflict.nr; i++) {
                struct string_list_item *it = &conflict.items[i];
-               if (!match_pathspec_depth(pathspec, it->string, strlen(it->string),
-                                         0, NULL))
+               if (!match_pathspec(pathspec, it->string,
+                                   strlen(it->string), 0, NULL, 0))
                        continue;
                rerere_forget_one_path(it->string, &merge_rr);
        }
index c09b00664e6892c84424bb4984152883460d3df6..44c697c36d0b406330aeb084db1d6a4be715d6c6 100644 (file)
@@ -119,6 +119,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
        struct string_list_item *item;
        struct resolve_undo_info *ru;
        int i, err = 0, matched;
+       char *name;
 
        if (!istate->resolve_undo)
                return pos;
@@ -138,20 +139,22 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
        if (!ru)
                return pos;
        matched = ce->ce_flags & CE_MATCHED;
+       name = xstrdup(ce->name);
        remove_index_entry_at(istate, pos);
        for (i = 0; i < 3; i++) {
                struct cache_entry *nce;
                if (!ru->mode[i])
                        continue;
                nce = make_cache_entry(ru->mode[i], ru->sha1[i],
-                                      ce->name, i + 1, 0);
+                                      name, i + 1, 0);
                if (matched)
                        nce->ce_flags |= CE_MATCHED;
                if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
                        err = 1;
-                       error("cannot unmerge '%s'", ce->name);
+                       error("cannot unmerge '%s'", name);
                }
        }
+       free(name);
        if (err)
                return pos;
        free(ru);
@@ -182,7 +185,7 @@ void unmerge_index(struct index_state *istate, const struct pathspec *pathspec)
 
        for (i = 0; i < istate->cache_nr; i++) {
                const struct cache_entry *ce = istate->cache[i];
-               if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+               if (!ce_path_match(ce, pathspec, NULL))
                        continue;
                i = unmerge_index_entry_at(istate, i);
        }
index a0df72f32c100a68e5d99b5b00e597d43f3802a5..bd027bc0152a59ac0c4584ab752ff70bee5abbfa 100644 (file)
@@ -16,6 +16,7 @@
 #include "line-log.h"
 #include "mailmap.h"
 #include "commit-slab.h"
+#include "dir.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -496,24 +497,14 @@ static int rev_compare_tree(struct rev_info *revs,
 static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
 {
        int retval;
-       void *tree;
-       unsigned long size;
-       struct tree_desc empty, real;
        struct tree *t1 = commit->tree;
 
        if (!t1)
                return 0;
 
-       tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
-       if (!tree)
-               return 0;
-       init_tree_desc(&real, tree, size);
-       init_tree_desc(&empty, "", 0);
-
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
-       retval = diff_tree(&empty, &real, "", &revs->pruning);
-       free(tree);
+       retval = diff_tree_sha1(NULL, t1->object.sha1, "", &revs->pruning);
 
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
 }
@@ -783,6 +774,10 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                return 0;
        commit->object.flags |= ADDED;
 
+       if (revs->include_check &&
+           !revs->include_check(commit, revs->include_check_data))
+               return 0;
+
        /*
         * If the commit is uninteresting, don't try to
         * prune parents - we want the maximal uninteresting
@@ -1400,7 +1395,7 @@ static void prepare_show_merge(struct rev_info *revs)
                const struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
-               if (ce_path_match(ce, &revs->prune_data)) {
+               if (ce_path_match(ce, &revs->prune_data, NULL)) {
                        prune_num++;
                        prune = xrealloc(prune, sizeof(*prune) * prune_num);
                        prune[prune_num-2] = ce->name;
index 88967d6a24df852453f93cb7bc466386347b0c45..1eb94c1548c019a4d41cb42af805918afbc237bf 100644 (file)
@@ -172,6 +172,8 @@ struct rev_info {
        unsigned long min_age;
        int min_parents;
        int max_parents;
+       int (*include_check)(struct commit *, void *);
+       void *include_check_data;
 
        /* diff info for patches and for paths limiting */
        struct diff_options diffopt;
index 90cac7b02eef1e9f681f5dc58bc26becaaf5ac12..bde5f047b01376e05255a55516d3bdd261f497ea 100644 (file)
@@ -392,11 +392,18 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 {
        struct argv_array array;
        int rc;
+       char *gpg_sign;
 
        argv_array_init(&array);
        argv_array_push(&array, "commit");
        argv_array_push(&array, "-n");
 
+       if (opts->gpg_sign) {
+               gpg_sign = xmalloc(3 + strlen(opts->gpg_sign));
+               sprintf(gpg_sign, "-S%s", opts->gpg_sign);
+               argv_array_push(&array, gpg_sign);
+               free(gpg_sign);
+       }
        if (opts->signoff)
                argv_array_push(&array, "-s");
        if (!opts->edit) {
@@ -808,6 +815,8 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
                opts->mainline = git_config_int(key, value);
        else if (!strcmp(key, "options.strategy"))
                git_config_string(&opts->strategy, key, value);
+       else if (!strcmp(key, "options.gpg-sign"))
+               git_config_string(&opts->gpg_sign, key, value);
        else if (!strcmp(key, "options.strategy-option")) {
                ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
                opts->xopts[opts->xopts_nr++] = xstrdup(value);
@@ -981,6 +990,8 @@ static void save_opts(struct replay_opts *opts)
        }
        if (opts->strategy)
                git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
+       if (opts->gpg_sign)
+               git_config_set_in_file(opts_file, "options.gpg-sign", opts->gpg_sign);
        if (opts->xopts) {
                int i;
                for (i = 0; i < opts->xopts_nr; i++)
index 1fc22dcabe132cd437e7b0d3c56588c8cd7e63f2..db43e9cf86dcc0914a674adca63303c44e2ccc24 100644 (file)
@@ -37,6 +37,8 @@ struct replay_opts {
 
        int mainline;
 
+       const char *gpg_sign;
+
        /* Merge strategy */
        const char *strategy;
        const char **xopts;
diff --git a/setup.c b/setup.c
index 6c3f85ff7a7935ed6f05ab5ab376a2d6c5ca2d46..613e3b3c13b3f09bce07c54dc440d03c0b5beb0c 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -5,6 +5,70 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
+/*
+ * The input parameter must contain an absolute path, and it must already be
+ * normalized.
+ *
+ * Find the part of an absolute path that lies inside the work tree by
+ * dereferencing symlinks outside the work tree, for example:
+ * /dir1/repo/dir2/file   (work tree is /dir1/repo)      -> dir2/file
+ * /dir/file              (work tree is /)               -> dir/file
+ * /dir/symlink1/symlink2 (symlink1 points to work tree) -> symlink2
+ * /dir/repolink/file     (repolink points to /dir/repo) -> file
+ * /dir/repo              (exactly equal to work tree)   -> (empty string)
+ */
+static int abspath_part_inside_repo(char *path)
+{
+       size_t len;
+       size_t wtlen;
+       char *path0;
+       int off;
+       const char *work_tree = get_git_work_tree();
+
+       if (!work_tree)
+               return -1;
+       wtlen = strlen(work_tree);
+       len = strlen(path);
+       off = 0;
+
+       /* check if work tree is already the prefix */
+       if (wtlen <= len && !strncmp(path, work_tree, wtlen)) {
+               if (path[wtlen] == '/') {
+                       memmove(path, path + wtlen + 1, len - wtlen);
+                       return 0;
+               } else if (path[wtlen - 1] == '/' || path[wtlen] == '\0') {
+                       /* work tree is the root, or the whole path */
+                       memmove(path, path + wtlen, len - wtlen + 1);
+                       return 0;
+               }
+               /* work tree might match beginning of a symlink to work tree */
+               off = wtlen;
+       }
+       path0 = path;
+       path += offset_1st_component(path) + off;
+
+       /* check each '/'-terminated level */
+       while (*path) {
+               path++;
+               if (*path == '/') {
+                       *path = '\0';
+                       if (strcmp(real_path(path0), work_tree) == 0) {
+                               memmove(path0, path + 1, len - (path - path0));
+                               return 0;
+                       }
+                       *path = '/';
+               }
+       }
+
+       /* check whole path */
+       if (strcmp(real_path(path0), work_tree) == 0) {
+               *path0 = '\0';
+               return 0;
+       }
+
+       return -1;
+}
+
 /*
  * Normalize "path", prepending the "prefix" for relative paths. If
  * remaining_prefix is not NULL, return the actual prefix still
@@ -22,11 +86,17 @@ char *prefix_path_gently(const char *prefix, int len,
        const char *orig = path;
        char *sanitized;
        if (is_absolute_path(orig)) {
-               const char *temp = real_path(path);
-               sanitized = xmalloc(len + strlen(temp) + 1);
-               strcpy(sanitized, temp);
+               sanitized = xmalloc(strlen(path) + 1);
                if (remaining_prefix)
                        *remaining_prefix = 0;
+               if (normalize_path_copy_len(sanitized, path, remaining_prefix)) {
+                       free(sanitized);
+                       return NULL;
+               }
+               if (abspath_part_inside_repo(sanitized)) {
+                       free(sanitized);
+                       return NULL;
+               }
        } else {
                sanitized = xmalloc(len + strlen(path) + 1);
                if (len)
@@ -34,26 +104,10 @@ char *prefix_path_gently(const char *prefix, int len,
                strcpy(sanitized + len, path);
                if (remaining_prefix)
                        *remaining_prefix = len;
-       }
-       if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix))
-               goto error_out;
-       if (is_absolute_path(orig)) {
-               size_t root_len, len, total;
-               const char *work_tree = get_git_work_tree();
-               if (!work_tree)
-                       goto error_out;
-               len = strlen(work_tree);
-               root_len = offset_1st_component(work_tree);
-               total = strlen(sanitized) + 1;
-               if (strncmp(sanitized, work_tree, len) ||
-                   (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
-               error_out:
+               if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) {
                        free(sanitized);
                        return NULL;
                }
-               if (sanitized[len] == '/')
-                       len++;
-               memmove(sanitized, sanitized + len, total - len);
        }
        return sanitized;
 }
@@ -787,3 +841,27 @@ void sanitize_stdfds(void)
        if (fd > 2)
                close(fd);
 }
+
+int daemonize(void)
+{
+#ifdef NO_POSIX_GOODIES
+       errno = ENOSYS;
+       return -1;
+#else
+       switch (fork()) {
+               case 0:
+                       break;
+               case -1:
+                       die_errno("fork failed");
+               default:
+                       exit(0);
+       }
+       if (setsid() == -1)
+               die_errno("setsid failed");
+       close(0);
+       close(1);
+       close(2);
+       sanitize_stdfds();
+       return 0;
+#endif
+}
index 6e8c05d10825ee93d8248f749d4bef513d772be4..019628add50f4957c21f32abb99c06fc6e5f5d2c 100644 (file)
@@ -252,8 +252,6 @@ char *sha1_pack_index_name(const unsigned char *sha1)
 struct alternate_object_database *alt_odb_list;
 static struct alternate_object_database **alt_odb_tail;
 
-static int git_open_noatime(const char *name);
-
 /*
  * Prepare alternate object database registry.
  *
@@ -1232,6 +1230,7 @@ static void prepare_packed_git_one(char *objdir, int local)
 
                if (has_extension(de->d_name, ".idx") ||
                    has_extension(de->d_name, ".pack") ||
+                   has_extension(de->d_name, ".bitmap") ||
                    has_extension(de->d_name, ".keep"))
                        string_list_append(&garbage, path);
                else
@@ -1316,7 +1315,6 @@ void prepare_packed_git(void)
 
 void reprepare_packed_git(void)
 {
-       discard_revindex();
        prepare_packed_git_run_once = 0;
        prepare_packed_git();
 }
@@ -1393,7 +1391,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
        return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
-static int git_open_noatime(const char *name)
+int git_open_noatime(const char *name)
 {
        static int sha1_file_open_flag = O_NOATIME;
 
index 83caf4a9147e6788107fb5fd23e6d9e1d6f6267f..ee96dcfb816625436582833d812a7156513d5d39 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -10,15 +10,6 @@ int starts_with(const char *str, const char *prefix)
                        return 0;
 }
 
-int prefixcmp(const char *str, const char *prefix)
-{
-       for (; ; str++, prefix++)
-               if (!*prefix)
-                       return 0;
-               else if (*str != *prefix)
-                       return (unsigned char)*prefix - (unsigned char)*str;
-}
-
 int ends_with(const char *str, const char *suffix)
 {
        int len = strlen(str), suflen = strlen(suffix);
@@ -28,15 +19,6 @@ int ends_with(const char *str, const char *suffix)
                return !strcmp(str + len - suflen, suffix);
 }
 
-int suffixcmp(const char *str, const char *suffix)
-{
-       int len = strlen(str), suflen = strlen(suffix);
-       if (len < suflen)
-               return -1;
-       else
-               return strcmp(str + len - suflen, suffix);
-}
-
 /*
  * Used as the default ->buf value, so that people can always assume
  * buf is non NULL and ->buf is NUL terminated even for a freshly
index d7c9f32f0ce98112ddd4d4a8499b227b181e09e6..2ff036a0fa6c8e7b013d061d16baf33045305950 100644 (file)
@@ -152,8 +152,10 @@ struct git_istream *open_istream(const unsigned char *sha1,
        if (filter) {
                /* Add "&& !is_null_stream_filter(filter)" for performance */
                struct git_istream *nst = attach_stream_filter(st, filter);
-               if (!nst)
+               if (!nst) {
                        close_istream(st);
+                       return NULL;
+               }
                st = nst;
        }
 
index 613857e400f826f3d4ea28c87025b2c2df42408a..b80ecacf60dc87025336b6b6d6a51acc9321f429 100644 (file)
@@ -116,30 +116,7 @@ int remove_path_from_gitmodules(const char *path)
 
 void stage_updated_gitmodules(void)
 {
-       struct strbuf buf = STRBUF_INIT;
-       struct stat st;
-       int pos;
-       struct cache_entry *ce;
-       int namelen = strlen(".gitmodules");
-
-       pos = cache_name_pos(".gitmodules", namelen);
-       if (pos < 0) {
-               warning(_("could not find .gitmodules in index"));
-               return;
-       }
-       ce = active_cache[pos];
-       ce->ce_flags = namelen;
-       if (strbuf_read_file(&buf, ".gitmodules", 0) < 0)
-               die(_("reading updated .gitmodules failed"));
-       if (lstat(".gitmodules", &st) < 0)
-               die_errno(_("unable to stat updated .gitmodules"));
-       fill_stat_cache_info(ce, &st);
-       ce->ce_mode = ce_mode_from_stat(ce, st.st_mode);
-       if (remove_cache_entry_at(pos) < 0)
-               die(_("unable to remove .gitmodules from index"));
-       if (write_sha1_file(buf.buf, buf.len, blob_type, ce->sha1))
-               die(_("adding updated .gitmodules failed"));
-       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
+       if (add_file_to_cache(".gitmodules", 0))
                die(_("staging updated .gitmodules failed"));
 }
 
index 394b06b32f838463221e9c2f49380fd9eb08bb9d..bc4b3412fbca1f545ebdcaf23d3ef83d124ddc71 100644 (file)
 #      stop_git_daemon
 #      test_done
 
-if test -z "$GIT_TEST_GIT_DAEMON"
+test_tristate GIT_TEST_GIT_DAEMON
+if test "$GIT_TEST_GIT_DAEMON" = false
 then
-       skip_all="git-daemon testing disabled (define GIT_TEST_GIT_DAEMON to enable)"
+       skip_all="git-daemon testing disabled (unset GIT_TEST_GIT_DAEMON to enable)"
        test_done
 fi
 
-LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-'8121'}
+LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-${this_test#t}}
 
 GIT_DAEMON_PID=
 GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
@@ -58,7 +59,8 @@ start_git_daemon() {
                kill "$GIT_DAEMON_PID"
                wait "$GIT_DAEMON_PID"
                trap 'die' EXIT
-               error "git daemon failed to start"
+               test_skip_or_die $GIT_TEST_GIT_DAEMON \
+                       "git daemon failed to start"
        fi
 }
 
index bfdff2a8c93091c8e2db8e03a74eae5c5a53991c..252cbf163bcff90aabe39a48219e00565f577ffe 100644 (file)
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
 #
 
-if test -z "$GIT_TEST_HTTPD"
+test_tristate GIT_TEST_HTTPD
+if test "$GIT_TEST_HTTPD" = false
 then
-       skip_all="Network testing disabled (define GIT_TEST_HTTPD to enable)"
+       skip_all="Network testing disabled (unset GIT_TEST_HTTPD to enable)"
        test_done
 fi
 
@@ -64,7 +65,7 @@ case $(uname) in
 esac
 
 LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-${this_test#t}}
 
 TEST_PATH="$TEST_DIRECTORY"/lib-httpd
 HTTPD_ROOT_PATH="$PWD"/httpd
@@ -76,8 +77,7 @@ GIT_VALGRIND_OPTIONS=$GIT_VALGRIND_OPTIONS; export GIT_VALGRIND_OPTIONS
 
 if ! test -x "$LIB_HTTPD_PATH"
 then
-       skip_all="skipping test, no web server found at '$LIB_HTTPD_PATH'"
-       test_done
+       test_skip_or_die $GIT_TEST_HTTPD "no web server found at '$LIB_HTTPD_PATH'"
 fi
 
 HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \
@@ -89,19 +89,20 @@ then
        then
                if ! test $HTTPD_VERSION -ge 2
                then
-                       skip_all="skipping test, at least Apache version 2 is required"
-                       test_done
+                       test_skip_or_die $GIT_TEST_HTTPD \
+                               "at least Apache version 2 is required"
                fi
                if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
                then
-                       skip_all="Apache module directory not found.  Skipping tests."
-                       test_done
+                       test_skip_or_die $GIT_TEST_HTTPD \
+                               "Apache module directory not found"
                fi
 
                LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH"
        fi
 else
-       error "Could not identify web server at '$LIB_HTTPD_PATH'"
+       test_skip_or_die $GIT_TEST_HTTPD \
+               "Could not identify web server at '$LIB_HTTPD_PATH'"
 fi
 
 prepare_httpd() {
@@ -155,9 +156,8 @@ start_httpd() {
                >&3 2>&4
        if test $? -ne 0
        then
-               skip_all="skipping test, web server setup failed"
                trap 'die' EXIT
-               test_done
+               test_skip_or_die $GIT_TEST_HTTPD "web server setup failed"
        fi
 }
 
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
new file mode 100755 (executable)
index 0000000..685d46f
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='Tests pack performance using bitmaps'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+# note that we do everything through config,
+# since we want to be able to compare bitmap-aware
+# git versus non-bitmap git
+test_expect_success 'setup bitmap config' '
+       git config pack.writebitmaps true &&
+       git config pack.writebitmaphashcache true
+'
+
+test_perf 'repack to disk' '
+       git repack -ad
+'
+
+test_perf 'simulated clone' '
+       git pack-objects --stdout --all </dev/null >/dev/null
+'
+
+test_perf 'simulated fetch' '
+       have=$(git rev-list HEAD~100 -1) &&
+       {
+               echo HEAD &&
+               echo ^$have
+       } | git pack-objects --revs --stdout >/dev/null
+'
+
+test_expect_success 'create partial bitmap state' '
+       # pick a commit to represent the repo tip in the past
+       cutoff=$(git rev-list HEAD~100 -1) &&
+       orig_tip=$(git rev-parse HEAD) &&
+
+       # now kill off all of the refs and pretend we had
+       # just the one tip
+       rm -rf .git/logs .git/refs/* .git/packed-refs
+       git update-ref HEAD $cutoff
+
+       # and then repack, which will leave us with a nice
+       # big bitmap pack of the "old" history, and all of
+       # the new history will be loose, as if it had been pushed
+       # up incrementally and exploded via unpack-objects
+       git repack -Ad
+
+       # and now restore our original tip, as if the pushes
+       # had happened
+       git update-ref HEAD $orig_tip
+'
+
+test_perf 'partial bitmap' '
+       git pack-objects --stdout --all </dev/null >/dev/null
+'
+
+test_done
index b9d79476e20b4ddf0f9e85c672df2061fb910f9d..f0fbb425545019a6275a347a5826f6ba2977d394 100755 (executable)
@@ -243,40 +243,58 @@ EOF
        test_line_count = 0 err
 '
 
+test_expect_success 'using --git-dir and --work-tree' '
+       mkdir unreal real &&
+       git init real &&
+       echo "file test=in-real" >real/.gitattributes &&
+       (
+               cd unreal &&
+               attr_check file in-real "--git-dir ../real/.git --work-tree ../real"
+       )
+'
+
 test_expect_success 'setup bare' '
-       git clone --bare . bare.git &&
-       cd bare.git
+       git clone --bare . bare.git
 '
 
 test_expect_success 'bare repository: check that .gitattribute is ignored' '
        (
-               echo "f test=f"
-               echo "a/i test=a/i"
-       ) >.gitattributes &&
-       attr_check f unspecified &&
-       attr_check a/f unspecified &&
-       attr_check a/c/f unspecified &&
-       attr_check a/i unspecified &&
-       attr_check subdir/a/i unspecified
+               cd bare.git &&
+               (
+                       echo "f test=f"
+                       echo "a/i test=a/i"
+               ) >.gitattributes &&
+               attr_check f unspecified &&
+               attr_check a/f unspecified &&
+               attr_check a/c/f unspecified &&
+               attr_check a/i unspecified &&
+               attr_check subdir/a/i unspecified
+       )
 '
 
 test_expect_success 'bare repository: check that --cached honors index' '
-       GIT_INDEX_FILE=../.git/index \
-       git check-attr --cached --stdin --all <../stdin-all |
-       sort >actual &&
-       test_cmp ../specified-all actual
+       (
+               cd bare.git &&
+               GIT_INDEX_FILE=../.git/index \
+               git check-attr --cached --stdin --all <../stdin-all |
+               sort >actual &&
+               test_cmp ../specified-all actual
+       )
 '
 
 test_expect_success 'bare repository: test info/attributes' '
        (
-               echo "f test=f"
-               echo "a/i test=a/i"
-       ) >info/attributes &&
-       attr_check f f &&
-       attr_check a/f f &&
-       attr_check a/c/f f &&
-       attr_check a/i a/i &&
-       attr_check subdir/a/i unspecified
+               cd bare.git &&
+               (
+                       echo "f test=f"
+                       echo "a/i test=a/i"
+               ) >info/attributes &&
+               attr_check f f &&
+               attr_check a/f f &&
+               attr_check a/c/f f &&
+               attr_check a/i a/i &&
+               attr_check subdir/a/i unspecified
+       )
 '
 
 test_done
index b4d98e602f7422009599e58f0a7ae943476b1c7e..63beb99828cc17d2ec8218a6470b62c02a78806b 100755 (executable)
@@ -775,4 +775,35 @@ test_expect_success PIPE 'streaming support for --stdin' '
        echo "$response" | grep "^::    two"
 '
 
+############################################################################
+#
+# test whitespace handling
+
+test_expect_success 'trailing whitespace is ignored' '
+       mkdir whitespace &&
+       >whitespace/trailing &&
+       >whitespace/untracked &&
+       echo "whitespace/trailing   " >ignore &&
+       cat >expect <<EOF &&
+whitespace/untracked
+EOF
+       : >err.expect &&
+       git ls-files -o -X ignore whitespace >actual 2>err &&
+       test_cmp expect actual &&
+       test_cmp err.expect err
+'
+
+test_expect_success !MINGW 'quoting allows trailing whitespace' '
+       rm -rf whitespace &&
+       mkdir whitespace &&
+       >"whitespace/trailing  " &&
+       >whitespace/untracked &&
+       echo "whitespace/trailing\\ \\ " >ignore &&
+       echo whitespace/untracked >expect &&
+       : >err.expect &&
+       git ls-files -o -X ignore whitespace >actual 2>err &&
+       test_cmp expect actual &&
+       test_cmp err.expect err
+'
+
 test_done
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
new file mode 100755 (executable)
index 0000000..391e2b6
--- /dev/null
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+test_description='test hashmap and string hash functions'
+. ./test-lib.sh
+
+test_hashmap() {
+       echo "$1" | test-hashmap $3 > actual &&
+       echo "$2" > expect &&
+       test_cmp expect actual
+}
+
+test_expect_success 'hash functions' '
+
+test_hashmap "hash key1" "2215982743 2215982743 116372151 116372151" &&
+test_hashmap "hash key2" "2215982740 2215982740 116372148 116372148" &&
+test_hashmap "hash fooBarFrotz" "1383912807 1383912807 3189766727 3189766727" &&
+test_hashmap "hash foobarfrotz" "2862305959 2862305959 3189766727 3189766727"
+
+'
+
+test_expect_success 'put' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+put foobarfrotz value4
+size" "NULL
+NULL
+NULL
+NULL
+64 4"
+
+'
+
+test_expect_success 'put (case insensitive)' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+size" "NULL
+NULL
+NULL
+64 3" ignorecase
+
+'
+
+test_expect_success 'replace' '
+
+test_hashmap "put key1 value1
+put key1 value2
+put fooBarFrotz value3
+put fooBarFrotz value4
+size" "NULL
+value1
+NULL
+value3
+64 2"
+
+'
+
+test_expect_success 'replace (case insensitive)' '
+
+test_hashmap "put key1 value1
+put Key1 value2
+put fooBarFrotz value3
+put foobarfrotz value4
+size" "NULL
+value1
+NULL
+value3
+64 2" ignorecase
+
+'
+
+test_expect_success 'get' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+put foobarfrotz value4
+get key1
+get key2
+get fooBarFrotz
+get notInMap" "NULL
+NULL
+NULL
+NULL
+value1
+value2
+value3
+NULL"
+
+'
+
+test_expect_success 'get (case insensitive)' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+get Key1
+get keY2
+get foobarfrotz
+get notInMap" "NULL
+NULL
+NULL
+value1
+value2
+value3
+NULL" ignorecase
+
+'
+
+test_expect_success 'add' '
+
+test_hashmap "add key1 value1
+add key1 value2
+add fooBarFrotz value3
+add fooBarFrotz value4
+get key1
+get fooBarFrotz
+get notInMap" "value2
+value1
+value4
+value3
+NULL"
+
+'
+
+test_expect_success 'add (case insensitive)' '
+
+test_hashmap "add key1 value1
+add Key1 value2
+add fooBarFrotz value3
+add foobarfrotz value4
+get key1
+get Foobarfrotz
+get notInMap" "value2
+value1
+value4
+value3
+NULL" ignorecase
+
+'
+
+test_expect_success 'remove' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+remove key1
+remove key2
+remove notInMap
+size" "NULL
+NULL
+NULL
+value1
+value2
+NULL
+64 1"
+
+'
+
+test_expect_success 'remove (case insensitive)' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+remove Key1
+remove keY2
+remove notInMap
+size" "NULL
+NULL
+NULL
+value1
+value2
+NULL
+64 1" ignorecase
+
+'
+
+test_expect_success 'iterate' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+iterate" "NULL
+NULL
+NULL
+key2 value2
+key1 value1
+fooBarFrotz value3"
+
+'
+
+test_expect_success 'iterate (case insensitive)' '
+
+test_hashmap "put key1 value1
+put key2 value2
+put fooBarFrotz value3
+iterate" "NULL
+NULL
+NULL
+fooBarFrotz value3
+key2 value2
+key1 value1" ignorecase
+
+'
+
+test_expect_success 'grow / shrink' '
+
+       rm -f in &&
+       rm -f expect &&
+       for n in $(test_seq 51)
+       do
+               echo put key$n value$n >> in &&
+               echo NULL >> expect
+       done &&
+       echo size >> in &&
+       echo 64 51 >> expect &&
+       echo put key52 value52 >> in &&
+       echo NULL >> expect
+       echo size >> in &&
+       echo 256 52 >> expect &&
+       for n in $(test_seq 12)
+       do
+               echo remove key$n >> in &&
+               echo value$n >> expect
+       done &&
+       echo size >> in &&
+       echo 256 40 >> expect &&
+       echo remove key40 >> in &&
+       echo value40 >> expect &&
+       echo size >> in &&
+       echo 64 39 >> expect &&
+       cat in | test-hashmap > out &&
+       test_cmp expect out
+
+'
+
+test_done
index 07c10c8dca80b9d289431a72d5bf120ca442113d..c0143a0a70b7d3dc213b691aecea6b3fffbc5844 100755 (executable)
@@ -190,6 +190,27 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
        test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
+test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
+       ln -s target symlink &&
+       test "$(test-path-utils prefix_path prefix "$(pwd)/symlink")" = "symlink"
+'
+
+test_expect_success 'prefix_path works with only absolute path to work tree' '
+       echo "" >expected &&
+       test-path-utils prefix_path prefix "$(pwd)" >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'prefix_path rejects absolute path to dir with same beginning as work tree' '
+       test_must_fail test-path-utils prefix_path prefix "$(pwd)a"
+'
+
+test_expect_success SYMLINKS 'prefix_path works with absolute path to a symlink to work tree having  same beginning as work tree' '
+       git init repo &&
+       ln -s repo repolink &&
+       test "a" = "$(cd repo && test-path-utils prefix_path prefix "$(pwd)/../repolink/a")"
+'
+
 relative_path /foo/a/b/c/      /foo/a/b/       c/
 relative_path /foo/a/b/c/      /foo/a/b        c/
 relative_path /foo/a//b//c/    ///foo/a/b//    c/              POSIX
index 967359344dab8118dfd55816ef08f4809a9f33ad..c9c426c273e556302338f7a30fc46de64b856eb2 100755 (executable)
@@ -475,15 +475,28 @@ ein.bahn=strasse
 EOF
 
 test_expect_success 'alternative GIT_CONFIG' '
-       GIT_CONFIG=other-config git config -l >output &&
+       GIT_CONFIG=other-config git config --list >output &&
        test_cmp expect output
 '
 
 test_expect_success 'alternative GIT_CONFIG (--file)' '
-       git config --file other-config -l > output &&
+       git config --file other-config --list >output &&
        test_cmp expect output
 '
 
+test_expect_success 'alternative GIT_CONFIG (--file=-)' '
+       git config --file - --list <other-config >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'setting a value in stdin is an error' '
+       test_must_fail git config --file - some.value foo
+'
+
+test_expect_success 'editing stdin is an error' '
+       test_must_fail git config --file - --edit
+'
+
 test_expect_success 'refer config from subdirectory' '
        mkdir x &&
        (
index a70707620f146d3fce69f77e08cae3a47253f157..9ba2ba11c3cac88154dbb2d522b43adf98a8dee7 100755 (executable)
@@ -113,7 +113,7 @@ test_expect_success 'missing include files are ignored' '
 test_expect_success 'absolute includes from command line work' '
        echo "[test]one = 1" >one &&
        echo 1 >expect &&
-       git -c include.path="$PWD/one" config test.one >actual &&
+       git -c include.path="$(pwd)/one" config test.one >actual &&
        test_cmp expect actual
 '
 
@@ -122,6 +122,36 @@ test_expect_success 'relative includes from command line fail' '
        test_must_fail git -c include.path=one config test.one
 '
 
+test_expect_success 'absolute includes from blobs work' '
+       echo "[test]one = 1" >one &&
+       echo "[include]path=$(pwd)/one" >blob &&
+       blob=$(git hash-object -w blob) &&
+       echo 1 >expect &&
+       git config --blob=$blob test.one >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'relative includes from blobs fail' '
+       echo "[test]one = 1" >one &&
+       echo "[include]path=one" >blob &&
+       blob=$(git hash-object -w blob) &&
+       test_must_fail git config --blob=$blob test.one
+'
+
+test_expect_success 'absolute includes from stdin work' '
+       echo "[test]one = 1" >one &&
+       echo 1 >expect &&
+       echo "[include]path=\"$(pwd)/one\"" |
+       git config --file - test.one >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'relative includes from stdin line fail' '
+       echo "[test]one = 1" >one &&
+       echo "[include]path=one" |
+       test_must_fail git config --file - test.one
+'
+
 test_expect_success 'include cycles are detected' '
        cat >.gitconfig <<-\EOF &&
        [test]value = gitconfig
index 9bf2bdffd24b773a2eec636efdd534269f45e74b..e16b15d3e5784eb3c6423318142cb91fe3a58601 100755 (executable)
@@ -80,18 +80,13 @@ test_expect_success 'change gets noticed' '
 
 '
 
-# Note that this is scheduled to change in Git 2.0, when
-# "git add -u" will become full-tree by default.
-test_expect_success 'non-limited update in subdir leaves root alone' '
+test_expect_success 'non-qualified update in subdir updates from the root' '
        (
                cd dir1 &&
                echo even more >>sub2 &&
                git add -u
        ) &&
-       cat >expect <<-\EOF &&
-       check
-       top
-       EOF
+       : >expect &&
        git diff-files --name-only >actual &&
        test_cmp expect actual
 '
index 8d9bc3c2af6ad84c006010ee7be65146107c2259..9c7adbdbe1cd7bf03e9b8f10a75f0acb4f37a5b2 100755 (executable)
@@ -36,4 +36,21 @@ test_expect_success 'ls-files -h in corrupt repository' '
        test_i18ngrep "[Uu]sage: git ls-files " broken/usage
 '
 
+test_expect_success SYMLINKS 'ls-files with absolute paths to symlinks' '
+       mkdir subs &&
+       ln -s nosuch link &&
+       ln -s ../nosuch subs/link &&
+       git add link subs/link &&
+       git ls-files -s link subs/link >expect &&
+       git ls-files -s "$(pwd)/link" "$(pwd)/subs/link" >actual &&
+       test_cmp expect actual &&
+
+       (
+               cd subs &&
+               git ls-files -s link >../expect &&
+               git ls-files -s "$(pwd)/link" >../actual
+       ) &&
+       test_cmp expect actual
+'
+
 test_done
index 2f96100a5f655bbea859bf596ff9835c9abb11a2..82e18548c39817dec496766d2c8d500668fc4f76 100755 (executable)
@@ -257,6 +257,7 @@ test_expect_success 'setup 8' '
        git add e &&
        test_tick &&
        git commit -m "rename a->e" &&
+       c7=$(git rev-parse --verify HEAD) &&
        git checkout rename-ln &&
        git mv a e &&
        test_ln_s_add e a &&
@@ -517,6 +518,52 @@ test_expect_success 'reset and bind merge' '
 
 '
 
+test_expect_success 'merge-recursive w/ empty work tree - ours has rename' '
+       (
+               GIT_WORK_TREE="$PWD/ours-has-rename-work" &&
+               export GIT_WORK_TREE &&
+               GIT_INDEX_FILE="$PWD/ours-has-rename-index" &&
+               export GIT_INDEX_FILE &&
+               mkdir "$GIT_WORK_TREE" &&
+               git read-tree -i -m $c7 &&
+               git update-index --ignore-missing --refresh &&
+               git merge-recursive $c0 -- $c7 $c3 &&
+               git ls-files -s >actual-files
+       ) 2>actual-err &&
+       >expected-err &&
+       cat >expected-files <<-EOF &&
+       100644 $o3 0    b/c
+       100644 $o0 0    c
+       100644 $o0 0    d/e
+       100644 $o0 0    e
+       EOF
+       test_cmp expected-files actual-files &&
+       test_cmp expected-err actual-err
+'
+
+test_expect_success 'merge-recursive w/ empty work tree - theirs has rename' '
+       (
+               GIT_WORK_TREE="$PWD/theirs-has-rename-work" &&
+               export GIT_WORK_TREE &&
+               GIT_INDEX_FILE="$PWD/theirs-has-rename-index" &&
+               export GIT_INDEX_FILE &&
+               mkdir "$GIT_WORK_TREE" &&
+               git read-tree -i -m $c3 &&
+               git update-index --ignore-missing --refresh &&
+               git merge-recursive $c0 -- $c3 $c7 &&
+               git ls-files -s >actual-files
+       ) 2>actual-err &&
+       >expected-err &&
+       cat >expected-files <<-EOF &&
+       100644 $o3 0    b/c
+       100644 $o0 0    c
+       100644 $o0 0    d/e
+       100644 $o0 0    e
+       EOF
+       test_cmp expected-files actual-files &&
+       test_cmp expected-err actual-err
+'
+
 test_expect_success 'merge removes empty directories' '
 
        git reset --hard master &&
index 9f5659f7fe4df23d805e6c38264a1ebf38fc2538..2bb973655bf043cc43292764ffd68becda25aa2e 100755 (executable)
@@ -140,4 +140,10 @@ test_expect_success 'diff multiple wildcard pathspecs' '
        test_cmp expect actual
 '
 
+test_expect_success 'diff-cache ignores trailing slash on submodule path' '
+       git diff --name-only HEAD^ submod >expect &&
+       git diff --name-only HEAD^ submod/ >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 231412d1008e45e1d788660930cab708d3912ed4..e8ae2a03fdcf5ac57dc0e5bcacd40501960bce1c 100755 (executable)
@@ -148,4 +148,10 @@ test_expect_success 'git diff --ignore-all-space, both files outside repo' '
        )
 '
 
+test_expect_success 'git diff --quiet ignores stat-change only entries' '
+       test-chmtime +10 a &&
+       echo modified >>b &&
+       test_expect_code 1 git diff --quiet
+'
+
 test_done
index 9e2b29ede508e3d0822f5eb607db2ae4896611e6..c0460bb0e59c42de179fc6cbe0f3702a3b538a8e 100755 (executable)
@@ -97,4 +97,25 @@ do
        '
 done
 
+test_expect_success 'setup for testing combine-diff order' '
+       git checkout -b tmp HEAD~ &&
+       create_files 3 &&
+       git checkout master &&
+       git merge --no-commit -s ours tmp &&
+       create_files 5
+'
+
+test_expect_success "combine-diff: no order (=tree object order)" '
+       git diff --name-only HEAD HEAD^ HEAD^2 >actual &&
+       test_cmp expect_none actual
+'
+
+for i in 1 2
+do
+       test_expect_success "combine-diff: orderfile using option ($i)" '
+               git diff -Oorder_file_$i --name-only HEAD HEAD^ HEAD^2 >actual &&
+               test_cmp expect_$i actual
+       '
+done
+
 test_done
diff --git a/t/t4057-diff-combined-paths.sh b/t/t4057-diff-combined-paths.sh
new file mode 100755 (executable)
index 0000000..097e632
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+test_description='combined diff show only paths that are different to all parents'
+
+. ./test-lib.sh
+
+# verify that diffc.expect matches output of
+# `git diff -c --name-only HEAD HEAD^ HEAD^2`
+diffc_verify () {
+       git diff -c --name-only HEAD HEAD^ HEAD^2 >diffc.actual &&
+       test_cmp diffc.expect diffc.actual
+}
+
+test_expect_success 'trivial merge - combine-diff empty' '
+       for i in $(test_seq 1 9)
+       do
+               echo $i >$i.txt &&
+               git add $i.txt
+       done &&
+       git commit -m "init" &&
+       git checkout -b side &&
+       for i in $(test_seq 2 9)
+       do
+               echo $i/2 >>$i.txt
+       done &&
+       git commit -a -m "side 2-9" &&
+       git checkout master &&
+       echo 1/2 >1.txt &&
+       git commit -a -m "master 1" &&
+       git merge side &&
+       >diffc.expect &&
+       diffc_verify
+'
+
+
+test_expect_success 'only one trully conflicting path' '
+       git checkout side &&
+       for i in $(test_seq 2 9)
+       do
+               echo $i/3 >>$i.txt
+       done &&
+       echo "4side" >>4.txt &&
+       git commit -a -m "side 2-9 +4" &&
+       git checkout master &&
+       for i in $(test_seq 1 9)
+       do
+               echo $i/3 >>$i.txt
+       done &&
+       echo "4master" >>4.txt &&
+       git commit -a -m "master 1-9 +4" &&
+       test_must_fail git merge side &&
+       cat <<-\EOF >4.txt &&
+       4
+       4/2
+       4/3
+       4master
+       4side
+       EOF
+       git add 4.txt &&
+       git commit -m "merge side (2)" &&
+       echo 4.txt >diffc.expect &&
+       diffc_verify
+'
+
+test_expect_success 'merge introduces new file' '
+       git checkout side &&
+       for i in $(test_seq 5 9)
+       do
+               echo $i/4 >>$i.txt
+       done &&
+       git commit -a -m "side 5-9" &&
+       git checkout master &&
+       for i in $(test_seq 1 3)
+       do
+               echo $i/4 >>$i.txt
+       done &&
+       git commit -a -m "master 1-3 +4hello" &&
+       git merge side &&
+       echo "Hello World" >4hello.txt &&
+       git add 4hello.txt &&
+       git commit --amend &&
+       echo 4hello.txt >diffc.expect &&
+       diffc_verify
+'
+
+test_expect_success 'merge removed a file' '
+       git checkout side &&
+       for i in $(test_seq 5 9)
+       do
+               echo $i/5 >>$i.txt
+       done &&
+       git commit -a -m "side 5-9" &&
+       git checkout master &&
+       for i in $(test_seq 1 3)
+       do
+               echo $i/4 >>$i.txt
+       done &&
+       git commit -a -m "master 1-3" &&
+       git merge side &&
+       git rm 4.txt &&
+       git commit --amend &&
+       echo 4.txt >diffc.expect &&
+       diffc_verify
+'
+
+test_done
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
new file mode 100755 (executable)
index 0000000..d3a3afa
--- /dev/null
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='exercise basic bitmap functionality'
+. ./test-lib.sh
+
+test_expect_success 'setup repo with moderate-sized history' '
+       for i in $(test_seq 1 10); do
+               test_commit $i
+       done &&
+       git checkout -b other HEAD~5 &&
+       for i in $(test_seq 1 10); do
+               test_commit side-$i
+       done &&
+       git checkout master &&
+       blob=$(echo tagged-blob | git hash-object -w --stdin) &&
+       git tag tagged-blob $blob &&
+       git config pack.writebitmaps true &&
+       git config pack.writebitmaphashcache true
+'
+
+test_expect_success 'full repack creates bitmaps' '
+       git repack -ad &&
+       ls .git/objects/pack/ | grep bitmap >output &&
+       test_line_count = 1 output
+'
+
+test_expect_success 'rev-list --test-bitmap verifies bitmaps' '
+       git rev-list --test-bitmap HEAD
+'
+
+rev_list_tests() {
+       state=$1
+
+       test_expect_success "counting commits via bitmap ($state)" '
+               git rev-list --count HEAD >expect &&
+               git rev-list --use-bitmap-index --count HEAD >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "counting partial commits via bitmap ($state)" '
+               git rev-list --count HEAD~5..HEAD >expect &&
+               git rev-list --use-bitmap-index --count HEAD~5..HEAD >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "counting non-linear history ($state)" '
+               git rev-list --count other...master >expect &&
+               git rev-list --use-bitmap-index --count other...master >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "enumerate --objects ($state)" '
+               git rev-list --objects --use-bitmap-index HEAD >tmp &&
+               cut -d" " -f1 <tmp >tmp2 &&
+               sort <tmp2 >actual &&
+               git rev-list --objects HEAD >tmp &&
+               cut -d" " -f1 <tmp >tmp2 &&
+               sort <tmp2 >expect &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "bitmap --objects handles non-commit objects ($state)" '
+               git rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&
+               grep $blob actual
+       '
+}
+
+rev_list_tests 'full bitmap'
+
+test_expect_success 'clone from bitmapped repository' '
+       git clone --no-local --bare . clone.git &&
+       git rev-parse HEAD >expect &&
+       git --git-dir=clone.git rev-parse HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'setup further non-bitmapped commits' '
+       for i in $(test_seq 1 10); do
+               test_commit further-$i
+       done
+'
+
+rev_list_tests 'partial bitmap'
+
+test_expect_success 'fetch (partial bitmap)' '
+       git --git-dir=clone.git fetch origin master:master &&
+       git rev-parse HEAD >expect &&
+       git --git-dir=clone.git rev-parse HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'incremental repack cannot create bitmaps' '
+       test_commit more-1 &&
+       test_must_fail git repack -d
+'
+
+test_expect_success 'incremental repack can disable bitmaps' '
+       test_commit more-2 &&
+       git repack -d --no-write-bitmap-index
+'
+
+test_expect_success 'full repack, reusing previous bitmaps' '
+       git repack -ad &&
+       ls .git/objects/pack/ | grep bitmap >output &&
+       test_line_count = 1 output
+'
+
+test_expect_success 'fetch (full bitmap)' '
+       git --git-dir=clone.git fetch origin master:master &&
+       git rev-parse HEAD >expect &&
+       git --git-dir=clone.git rev-parse HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_lazy_prereq JGIT '
+       type jgit
+'
+
+test_expect_success JGIT 'we can read jgit bitmaps' '
+       git clone . compat-jgit &&
+       (
+               cd compat-jgit &&
+               rm -f .git/objects/pack/*.bitmap &&
+               jgit gc &&
+               git rev-list --test-bitmap HEAD
+       )
+'
+
+test_expect_success JGIT 'jgit can read our bitmaps' '
+       git clone . compat-us &&
+       (
+               cd compat-us &&
+               git repack -adb &&
+               # jgit gc will barf if it does not like our bitmaps
+               jgit gc
+       )
+'
+
+test_done
index 129fc88bd28e8ea85e81310aec860df808949447..0736bcbcd52a76ef3400c046e7d3558dd1a866c7 100755 (executable)
@@ -164,6 +164,7 @@ test_expect_success 'receive-pack runs auto-gc in remote repo' '
            # Set the child to auto-pack if more than one pack exists
            cd child &&
            git config gc.autopacklimit 1 &&
+           git config gc.autodetach false &&
            git branch test_auto_gc &&
            # And create a file that follows the temporary object naming
            # convention for the auto-gc to remove
index b0fa7387cbe082a72e26590be8e621c922e91eba..3ae9092f5c2511e075c1bf6c215ea773f738f1f9 100755 (executable)
@@ -173,32 +173,4 @@ EOF
        )
 '
 
-if test -n "$NO_CURL" -o -z "$GIT_TEST_HTTPD"; then
-       say 'skipping remaining tests, git built without http support'
-       test_done
-fi
-
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5537'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
-
-test_expect_success 'clone http repository' '
-       git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       git clone $HTTPD_URL/smart/repo.git clone &&
-       (
-       cd clone &&
-       git fsck &&
-       git log --format=%s origin/master >actual &&
-       cat <<EOF >expect &&
-7
-6
-5
-4
-3
-EOF
-       test_cmp expect actual
-       )
-'
-
-stop_httpd
 test_done
index 0a6e40f144a767cba03818cffca8b8b2aa6a16e2..8e54ac57462987567b11623d502ffc68fca3d9a0 100755 (executable)
@@ -126,7 +126,6 @@ if test -n "$NO_CURL" -o -z "$GIT_TEST_HTTPD"; then
        test_done
 fi
 
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5537'}
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh
new file mode 100755 (executable)
index 0000000..94553e1
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+test_description='fetch/clone from a shallow clone over http'
+
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       skip_all='skipping test, git built without http support'
+       test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+commit() {
+       echo "$1" >tracked &&
+       git add tracked &&
+       git commit -m "$1"
+}
+
+test_expect_success 'setup shallow clone' '
+       commit 1 &&
+       commit 2 &&
+       commit 3 &&
+       commit 4 &&
+       commit 5 &&
+       commit 6 &&
+       commit 7 &&
+       git clone --no-local --depth=5 .git shallow &&
+       git config --global transfer.fsckObjects true
+'
+
+test_expect_success 'clone http repository' '
+       git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git clone $HTTPD_URL/smart/repo.git clone &&
+       (
+       cd clone &&
+       git fsck &&
+       git log --format=%s origin/master >actual &&
+       cat <<EOF >expect &&
+7
+6
+5
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+# This test is tricky. We need large enough "have"s that fetch-pack
+# will put pkt-flush in between. Then we need a "have" the server
+# does not have, it'll send "ACK %s ready"
+test_expect_success 'no shallow lines after receiving ACK ready' '
+       (
+               cd shallow &&
+               for i in $(test_seq 15)
+               do
+                       git checkout --orphan unrelated$i &&
+                       test_commit unrelated$i &&
+                       git push -q "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+                               refs/heads/unrelated$i:refs/heads/unrelated$i &&
+                       git push -q ../clone/.git \
+                               refs/heads/unrelated$i:refs/heads/unrelated$i ||
+                       exit 1
+               done &&
+               git checkout master &&
+               test_commit new &&
+               git push  "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" master
+       ) &&
+       (
+               cd clone &&
+               git checkout --orphan newnew &&
+               test_commit new-too &&
+               GIT_TRACE_PACKET="$TRASH_DIRECTORY/trace" git fetch --depth=2 &&
+               grep "fetch-pack< ACK .* ready" ../trace &&
+               ! grep "fetch-pack> done" ../trace
+       )
+'
+
+stop_httpd
+test_done
diff --git a/t/t5540-http-push-webdav.sh b/t/t5540-http-push-webdav.sh
new file mode 100755 (executable)
index 0000000..8d7b3c5
--- /dev/null
@@ -0,0 +1,181 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test WebDAV http-push
+
+This test runs various sanity checks on http-push.'
+
+. ./test-lib.sh
+
+if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
+then
+       skip_all="skipping test, USE_CURL_MULTI is not defined"
+       test_done
+fi
+
+LIB_HTTPD_DAV=t
+. "$TEST_DIRECTORY"/lib-httpd.sh
+ROOT_PATH="$PWD"
+start_httpd
+
+test_expect_success 'setup remote repository' '
+       cd "$ROOT_PATH" &&
+       mkdir test_repo &&
+       cd test_repo &&
+       git init &&
+       : >path1 &&
+       git add path1 &&
+       test_tick &&
+       git commit -m initial &&
+       cd - &&
+       git clone --bare test_repo test_repo.git &&
+       cd test_repo.git &&
+       git --bare update-server-info &&
+       mv hooks/post-update.sample hooks/post-update &&
+       ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+       cd - &&
+       mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+test_expect_success 'create password-protected repository' '
+       mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb" &&
+       cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+              "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
+'
+
+setup_askpass_helper
+
+test_expect_success 'clone remote repository' '
+       cd "$ROOT_PATH" &&
+       git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository with packed refs' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       : >path2 &&
+       git add path2 &&
+       test_tick &&
+       git commit -m path2 &&
+       HEAD=$(git rev-parse --verify HEAD) &&
+       git push &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+       git push
+'
+
+test_expect_success 'push to remote repository with unpacked refs' '
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        rm packed-refs &&
+        git update-ref refs/heads/master $ORIG_HEAD &&
+        git --bare update-server-info) &&
+       git push &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'http-push fetches unpacked objects' '
+       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+               "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_unpacked.git &&
+
+       git clone $HTTPD_URL/dumb/test_repo_unpacked.git \
+               "$ROOT_PATH"/fetch_unpacked &&
+
+       # By reset, we force git to retrieve the object
+       (cd "$ROOT_PATH"/fetch_unpacked &&
+        git reset --hard HEAD^ &&
+        git remote rm origin &&
+        git reflog expire --expire=0 --all &&
+        git prune &&
+        git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git master)
+'
+
+test_expect_success 'http-push fetches packed objects' '
+       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+               "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
+
+       git clone $HTTPD_URL/dumb/test_repo_packed.git \
+               "$ROOT_PATH"/test_repo_clone_packed &&
+
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
+        git --bare repack &&
+        git --bare prune-packed) &&
+
+       # By reset, we force git to retrieve the packed object
+       (cd "$ROOT_PATH"/test_repo_clone_packed &&
+        git reset --hard HEAD^ &&
+        git remote remove origin &&
+        git reflog expire --expire=0 --all &&
+        git prune &&
+        git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
+'
+
+test_expect_success 'create and delete remote branch' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       git checkout -b dev &&
+       : >path3 &&
+       git add path3 &&
+       test_tick &&
+       git commit -m dev &&
+       git push origin dev &&
+       git push origin :dev &&
+       test_must_fail git show-ref --verify refs/remotes/origin/dev
+'
+
+test_expect_success 'MKCOL sends directory names with trailing slashes' '
+
+       ! grep "\"MKCOL.*[^/] HTTP/[^ ]*\"" < "$HTTPD_ROOT_PATH"/access.log
+
+'
+
+x1="[0-9a-f]"
+x2="$x1$x1"
+x5="$x1$x1$x1$x1$x1"
+x38="$x5$x5$x5$x5$x5$x5$x5$x1$x1$x1"
+x40="$x38$x2"
+
+test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
+       sed \
+               -e "s/PUT /OP /" \
+               -e "s/MOVE /OP /" \
+           -e "s|/objects/$x2/${x38}_$x40|WANTED_PATH_REQUEST|" \
+               "$HTTPD_ROOT_PATH"/access.log |
+       grep -e "\"OP .*WANTED_PATH_REQUEST HTTP/[.0-9]*\" 20[0-9] "
+
+'
+
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+       "$ROOT_PATH"/test_repo_clone master
+
+test_expect_success 'push to password-protected repository (user in URL)' '
+       test_commit pw-user &&
+       set_askpass user@host pass@host &&
+       git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
+       git rev-parse --verify HEAD >expect &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+               rev-parse --verify HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_failure 'user was prompted only once for password' '
+       expect_askpass pass user@host
+'
+
+test_expect_failure 'push to password-protected repository (no user in URL)' '
+       test_commit pw-nouser &&
+       set_askpass user@host pass@host &&
+       git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
+       expect_askpass both user@host
+       git rev-parse --verify HEAD >expect &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+               rev-parse --verify HEAD >actual &&
+       test_cmp expect actual
+'
+
+stop_httpd
+
+test_done
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
deleted file mode 100755 (executable)
index 5b0198c..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
-#
-
-test_description='test WebDAV http-push
-
-This test runs various sanity checks on http-push.'
-
-. ./test-lib.sh
-
-if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
-then
-       skip_all="skipping test, USE_CURL_MULTI is not defined"
-       test_done
-fi
-
-LIB_HTTPD_DAV=t
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-ROOT_PATH="$PWD"
-start_httpd
-
-test_expect_success 'setup remote repository' '
-       cd "$ROOT_PATH" &&
-       mkdir test_repo &&
-       cd test_repo &&
-       git init &&
-       : >path1 &&
-       git add path1 &&
-       test_tick &&
-       git commit -m initial &&
-       cd - &&
-       git clone --bare test_repo test_repo.git &&
-       cd test_repo.git &&
-       git --bare update-server-info &&
-       mv hooks/post-update.sample hooks/post-update &&
-       ORIG_HEAD=$(git rev-parse --verify HEAD) &&
-       cd - &&
-       mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
-'
-
-test_expect_success 'create password-protected repository' '
-       mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb" &&
-       cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
-              "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
-'
-
-setup_askpass_helper
-
-test_expect_success 'clone remote repository' '
-       cd "$ROOT_PATH" &&
-       git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
-'
-
-test_expect_success 'push to remote repository with packed refs' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       : >path2 &&
-       git add path2 &&
-       test_tick &&
-       git commit -m path2 &&
-       HEAD=$(git rev-parse --verify HEAD) &&
-       git push &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
-        test $HEAD = $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'push already up-to-date' '
-       git push
-'
-
-test_expect_success 'push to remote repository with unpacked refs' '
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
-        rm packed-refs &&
-        git update-ref refs/heads/master $ORIG_HEAD &&
-        git --bare update-server-info) &&
-       git push &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
-        test $HEAD = $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'http-push fetches unpacked objects' '
-       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
-               "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_unpacked.git &&
-
-       git clone $HTTPD_URL/dumb/test_repo_unpacked.git \
-               "$ROOT_PATH"/fetch_unpacked &&
-
-       # By reset, we force git to retrieve the object
-       (cd "$ROOT_PATH"/fetch_unpacked &&
-        git reset --hard HEAD^ &&
-        git remote rm origin &&
-        git reflog expire --expire=0 --all &&
-        git prune &&
-        git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git master)
-'
-
-test_expect_success 'http-push fetches packed objects' '
-       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
-               "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
-
-       git clone $HTTPD_URL/dumb/test_repo_packed.git \
-               "$ROOT_PATH"/test_repo_clone_packed &&
-
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
-        git --bare repack &&
-        git --bare prune-packed) &&
-
-       # By reset, we force git to retrieve the packed object
-       (cd "$ROOT_PATH"/test_repo_clone_packed &&
-        git reset --hard HEAD^ &&
-        git remote remove origin &&
-        git reflog expire --expire=0 --all &&
-        git prune &&
-        git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
-'
-
-test_expect_success 'create and delete remote branch' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       git checkout -b dev &&
-       : >path3 &&
-       git add path3 &&
-       test_tick &&
-       git commit -m dev &&
-       git push origin dev &&
-       git push origin :dev &&
-       test_must_fail git show-ref --verify refs/remotes/origin/dev
-'
-
-test_expect_success 'MKCOL sends directory names with trailing slashes' '
-
-       ! grep "\"MKCOL.*[^/] HTTP/[^ ]*\"" < "$HTTPD_ROOT_PATH"/access.log
-
-'
-
-x1="[0-9a-f]"
-x2="$x1$x1"
-x5="$x1$x1$x1$x1$x1"
-x38="$x5$x5$x5$x5$x5$x5$x5$x1$x1$x1"
-x40="$x38$x2"
-
-test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
-       sed \
-               -e "s/PUT /OP /" \
-               -e "s/MOVE /OP /" \
-           -e "s|/objects/$x2/${x38}_$x40|WANTED_PATH_REQUEST|" \
-               "$HTTPD_ROOT_PATH"/access.log |
-       grep -e "\"OP .*WANTED_PATH_REQUEST HTTP/[.0-9]*\" 20[0-9] "
-
-'
-
-test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
-       "$ROOT_PATH"/test_repo_clone master
-
-test_expect_success 'push to password-protected repository (user in URL)' '
-       test_commit pw-user &&
-       set_askpass user@host pass@host &&
-       git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
-       git rev-parse --verify HEAD >expect &&
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
-               rev-parse --verify HEAD >actual &&
-       test_cmp expect actual
-'
-
-test_expect_failure 'user was prompted only once for password' '
-       expect_askpass pass user@host
-'
-
-test_expect_failure 'push to password-protected repository (no user in URL)' '
-       test_commit pw-nouser &&
-       set_askpass user@host pass@host &&
-       git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
-       expect_askpass both user@host
-       git rev-parse --verify HEAD >expect &&
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
-               rev-parse --verify HEAD >actual &&
-       test_cmp expect actual
-'
-
-stop_httpd
-
-test_done
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
new file mode 100755 (executable)
index 0000000..73af16f
--- /dev/null
@@ -0,0 +1,327 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test smart pushing over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       skip_all='skipping test, git built without http support'
+       test_done
+fi
+
+ROOT_PATH="$PWD"
+. "$TEST_DIRECTORY"/lib-httpd.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+start_httpd
+
+test_expect_success 'setup remote repository' '
+       cd "$ROOT_PATH" &&
+       mkdir test_repo &&
+       cd test_repo &&
+       git init &&
+       : >path1 &&
+       git add path1 &&
+       test_tick &&
+       git commit -m initial &&
+       cd - &&
+       git clone --bare test_repo test_repo.git &&
+       cd test_repo.git &&
+       git config http.receivepack true &&
+       git config core.logallrefupdates true &&
+       ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+       cd - &&
+       mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+setup_askpass_helper
+
+cat >exp <<EOF
+GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'no empty path components' '
+       # In the URL, add a trailing slash, and see if git appends yet another
+       # slash.
+       cd "$ROOT_PATH" &&
+       git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
+
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+
+       # Clear the log, so that it does not affect the "used receive-pack
+       # service" test which reads the log too.
+       #
+       # We do this before the actual comparison to ensure the log is cleared.
+       echo > "$HTTPD_ROOT_PATH"/access.log &&
+
+       test_cmp exp act
+'
+
+test_expect_success 'clone remote repository' '
+       rm -rf test_repo_clone &&
+       git clone $HTTPD_URL/smart/test_repo.git test_repo_clone &&
+       (
+               cd test_repo_clone && git config push.default matching
+       )
+'
+
+test_expect_success 'push to remote repository (standard)' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       : >path2 &&
+       git add path2 &&
+       test_tick &&
+       git commit -m path2 &&
+       HEAD=$(git rev-parse --verify HEAD) &&
+       GIT_CURL_VERBOSE=1 git push -v -v 2>err &&
+       ! grep "Expect: 100-continue" err &&
+       grep "POST git-receive-pack ([0-9]* bytes)" err &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+       git push
+'
+
+test_expect_success 'create and delete remote branch' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       git checkout -b dev &&
+       : >path3 &&
+       git add path3 &&
+       test_tick &&
+       git commit -m dev &&
+       git push origin dev &&
+       git push origin :dev &&
+       test_must_fail git show-ref --verify refs/remotes/origin/dev
+'
+
+cat >"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" <<EOF
+#!/bin/sh
+exit 1
+EOF
+chmod a+x "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+
+cat >exp <<EOF
+remote: error: hook declined to update refs/heads/dev2
+To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
+ ! [remote rejected] dev2 -> dev2 (hook declined)
+error: failed to push some refs to 'http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'
+EOF
+
+test_expect_success 'rejected update prints status' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       git checkout -b dev2 &&
+       : >path4 &&
+       git add path4 &&
+       test_tick &&
+       git commit -m dev2 &&
+       test_must_fail git push origin dev2 2>act &&
+       sed -e "/^remote: /s/ *$//" <act >cmp &&
+       test_cmp exp cmp
+'
+rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+
+cat >exp <<EOF
+
+GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+EOF
+test_expect_success 'used receive-pack service' '
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+       test_cmp exp act
+'
+
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+       "$ROOT_PATH"/test_repo_clone master             success
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
+       # create a dissimilarly-named remote ref so that git is unable to match the
+       # two refs (viz. local, remote) unless an explicit refspec is provided.
+       git push origin master:retsam
+
+       echo "change changed" > path2 &&
+       git commit -a -m path2 --amend &&
+
+       # push master too; this ensures there is at least one '"'push'"' command to
+       # the remote helper and triggers interaction with the helper.
+       test_must_fail git push -v origin +master master:retsam >output 2>&1'
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' '
+       grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
+       grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output
+'
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
+       test_i18ngrep "Updates were rejected because" \
+               output
+'
+
+test_expect_success 'push (chunked)' '
+       git checkout master &&
+       test_commit commit path3 &&
+       HEAD=$(git rev-parse --verify HEAD) &&
+       test_config http.postbuffer 4 &&
+       git push -v -v origin $BRANCH 2>err &&
+       grep "POST git-receive-pack (chunked)" err &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push --all can push to empty repo' '
+       d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
+       git init --bare "$d" &&
+       git --git-dir="$d" config http.receivepack true &&
+       git push --all "$HTTPD_URL"/smart/empty-all.git
+'
+
+test_expect_success 'push --mirror can push to empty repo' '
+       d=$HTTPD_DOCUMENT_ROOT_PATH/empty-mirror.git &&
+       git init --bare "$d" &&
+       git --git-dir="$d" config http.receivepack true &&
+       git push --mirror "$HTTPD_URL"/smart/empty-mirror.git
+'
+
+test_expect_success 'push --all to repo with alternates' '
+       s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
+       d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-all.git &&
+       git clone --bare --shared "$s" "$d" &&
+       git --git-dir="$d" config http.receivepack true &&
+       git --git-dir="$d" repack -adl &&
+       git push --all "$HTTPD_URL"/smart/alternates-all.git
+'
+
+test_expect_success 'push --mirror to repo with alternates' '
+       s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
+       d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-mirror.git &&
+       git clone --bare --shared "$s" "$d" &&
+       git --git-dir="$d" config http.receivepack true &&
+       git --git-dir="$d" repack -adl &&
+       git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
+'
+
+test_expect_success TTY 'push shows progress when stderr is a tty' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit noisy &&
+       test_terminal git push >output 2>&1 &&
+       grep "^Writing objects" output
+'
+
+test_expect_success TTY 'push --quiet silences status and progress' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit quiet &&
+       test_terminal git push --quiet >output 2>&1 &&
+       test_cmp /dev/null output
+'
+
+test_expect_success TTY 'push --no-progress silences progress but not status' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit no-progress &&
+       test_terminal git push --no-progress >output 2>&1 &&
+       grep "^To http" output &&
+       ! grep "^Writing objects"
+'
+
+test_expect_success 'push --progress shows progress to non-tty' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit progress &&
+       git push --progress >output 2>&1 &&
+       grep "^To http" output &&
+       grep "^Writing objects" output
+'
+
+test_expect_success 'http push gives sane defaults to reflog' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit reflog-test &&
+       git push "$HTTPD_URL"/smart/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -g -1 --format="%gn <%ge>" >actual &&
+       echo "anonymous <anonymous@http.127.0.0.1>" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit custom-reflog-test &&
+       git push "$HTTPD_URL"/smart_custom_env/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -g -1 --format="%gn <%ge>" >actual &&
+       echo "Custom User <custom@example.com>" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push over smart http with auth' '
+       cd "$ROOT_PATH/test_repo_clone" &&
+       echo push-auth-test >expect &&
+       test_commit push-auth-test &&
+       set_askpass user@host pass@host &&
+       git push "$HTTPD_URL"/auth/smart/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -1 --format=%s >actual &&
+       expect_askpass both user@host &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to auth-only-for-push repo' '
+       cd "$ROOT_PATH/test_repo_clone" &&
+       echo push-half-auth >expect &&
+       test_commit push-half-auth &&
+       set_askpass user@host pass@host &&
+       git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -1 --format=%s >actual &&
+       expect_askpass both user@host &&
+       test_cmp expect actual
+'
+
+test_expect_success 'create repo without http.receivepack set' '
+       cd "$ROOT_PATH" &&
+       git init half-auth &&
+       (
+               cd half-auth &&
+               test_commit one
+       ) &&
+       git clone --bare half-auth "$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git"
+'
+
+test_expect_success 'clone via half-auth-complete does not need password' '
+       cd "$ROOT_PATH" &&
+       set_askpass wrong &&
+       git clone "$HTTPD_URL"/half-auth-complete/smart/half-auth.git \
+               half-auth-clone &&
+       expect_askpass none
+'
+
+test_expect_success 'push into half-auth-complete requires password' '
+       cd "$ROOT_PATH/half-auth-clone" &&
+       echo two >expect &&
+       test_commit two &&
+       set_askpass user@host pass@host &&
+       git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
+               log -1 --format=%s >actual &&
+       expect_askpass both user@host &&
+       test_cmp expect actual
+'
+
+stop_httpd
+test_done
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
deleted file mode 100755 (executable)
index bfd241e..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
-#
-
-test_description='test smart pushing over http via http-backend'
-. ./test-lib.sh
-
-if test -n "$NO_CURL"; then
-       skip_all='skipping test, git built without http support'
-       test_done
-fi
-
-ROOT_PATH="$PWD"
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5541'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-. "$TEST_DIRECTORY"/lib-terminal.sh
-start_httpd
-
-test_expect_success 'setup remote repository' '
-       cd "$ROOT_PATH" &&
-       mkdir test_repo &&
-       cd test_repo &&
-       git init &&
-       : >path1 &&
-       git add path1 &&
-       test_tick &&
-       git commit -m initial &&
-       cd - &&
-       git clone --bare test_repo test_repo.git &&
-       cd test_repo.git &&
-       git config http.receivepack true &&
-       git config core.logallrefupdates true &&
-       ORIG_HEAD=$(git rev-parse --verify HEAD) &&
-       cd - &&
-       mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
-'
-
-setup_askpass_helper
-
-cat >exp <<EOF
-GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
-EOF
-test_expect_success 'no empty path components' '
-       # In the URL, add a trailing slash, and see if git appends yet another
-       # slash.
-       cd "$ROOT_PATH" &&
-       git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
-
-       sed -e "
-               s/^.* \"//
-               s/\"//
-               s/ [1-9][0-9]*\$//
-               s/^GET /GET  /
-       " >act <"$HTTPD_ROOT_PATH"/access.log &&
-
-       # Clear the log, so that it does not affect the "used receive-pack
-       # service" test which reads the log too.
-       #
-       # We do this before the actual comparison to ensure the log is cleared.
-       echo > "$HTTPD_ROOT_PATH"/access.log &&
-
-       test_cmp exp act
-'
-
-test_expect_success 'clone remote repository' '
-       rm -rf test_repo_clone &&
-       git clone $HTTPD_URL/smart/test_repo.git test_repo_clone &&
-       (
-               cd test_repo_clone && git config push.default matching
-       )
-'
-
-test_expect_success 'push to remote repository (standard)' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       : >path2 &&
-       git add path2 &&
-       test_tick &&
-       git commit -m path2 &&
-       HEAD=$(git rev-parse --verify HEAD) &&
-       GIT_CURL_VERBOSE=1 git push -v -v 2>err &&
-       ! grep "Expect: 100-continue" err &&
-       grep "POST git-receive-pack ([0-9]* bytes)" err &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
-        test $HEAD = $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'push already up-to-date' '
-       git push
-'
-
-test_expect_success 'create and delete remote branch' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       git checkout -b dev &&
-       : >path3 &&
-       git add path3 &&
-       test_tick &&
-       git commit -m dev &&
-       git push origin dev &&
-       git push origin :dev &&
-       test_must_fail git show-ref --verify refs/remotes/origin/dev
-'
-
-cat >"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" <<EOF
-#!/bin/sh
-exit 1
-EOF
-chmod a+x "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
-
-cat >exp <<EOF
-remote: error: hook declined to update refs/heads/dev2
-To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
- ! [remote rejected] dev2 -> dev2 (hook declined)
-error: failed to push some refs to 'http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'
-EOF
-
-test_expect_success 'rejected update prints status' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       git checkout -b dev2 &&
-       : >path4 &&
-       git add path4 &&
-       test_tick &&
-       git commit -m dev2 &&
-       test_must_fail git push origin dev2 2>act &&
-       sed -e "/^remote: /s/ *$//" <act >cmp &&
-       test_cmp exp cmp
-'
-rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
-
-cat >exp <<EOF
-
-GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-EOF
-test_expect_success 'used receive-pack service' '
-       sed -e "
-               s/^.* \"//
-               s/\"//
-               s/ [1-9][0-9]*\$//
-               s/^GET /GET  /
-       " >act <"$HTTPD_ROOT_PATH"/access.log &&
-       test_cmp exp act
-'
-
-test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
-       "$ROOT_PATH"/test_repo_clone master             success
-
-test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
-       # create a dissimilarly-named remote ref so that git is unable to match the
-       # two refs (viz. local, remote) unless an explicit refspec is provided.
-       git push origin master:retsam
-
-       echo "change changed" > path2 &&
-       git commit -a -m path2 --amend &&
-
-       # push master too; this ensures there is at least one '"'push'"' command to
-       # the remote helper and triggers interaction with the helper.
-       test_must_fail git push -v origin +master master:retsam >output 2>&1'
-
-test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' '
-       grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
-       grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output
-'
-
-test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
-       test_i18ngrep "Updates were rejected because" \
-               output
-'
-
-test_expect_success 'push (chunked)' '
-       git checkout master &&
-       test_commit commit path3 &&
-       HEAD=$(git rev-parse --verify HEAD) &&
-       test_config http.postbuffer 4 &&
-       git push -v -v origin $BRANCH 2>err &&
-       grep "POST git-receive-pack (chunked)" err &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
-        test $HEAD = $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'push --all can push to empty repo' '
-       d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
-       git init --bare "$d" &&
-       git --git-dir="$d" config http.receivepack true &&
-       git push --all "$HTTPD_URL"/smart/empty-all.git
-'
-
-test_expect_success 'push --mirror can push to empty repo' '
-       d=$HTTPD_DOCUMENT_ROOT_PATH/empty-mirror.git &&
-       git init --bare "$d" &&
-       git --git-dir="$d" config http.receivepack true &&
-       git push --mirror "$HTTPD_URL"/smart/empty-mirror.git
-'
-
-test_expect_success 'push --all to repo with alternates' '
-       s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
-       d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-all.git &&
-       git clone --bare --shared "$s" "$d" &&
-       git --git-dir="$d" config http.receivepack true &&
-       git --git-dir="$d" repack -adl &&
-       git push --all "$HTTPD_URL"/smart/alternates-all.git
-'
-
-test_expect_success 'push --mirror to repo with alternates' '
-       s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
-       d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-mirror.git &&
-       git clone --bare --shared "$s" "$d" &&
-       git --git-dir="$d" config http.receivepack true &&
-       git --git-dir="$d" repack -adl &&
-       git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
-'
-
-test_expect_success TTY 'push shows progress when stderr is a tty' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       test_commit noisy &&
-       test_terminal git push >output 2>&1 &&
-       grep "^Writing objects" output
-'
-
-test_expect_success TTY 'push --quiet silences status and progress' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       test_commit quiet &&
-       test_terminal git push --quiet >output 2>&1 &&
-       test_cmp /dev/null output
-'
-
-test_expect_success TTY 'push --no-progress silences progress but not status' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       test_commit no-progress &&
-       test_terminal git push --no-progress >output 2>&1 &&
-       grep "^To http" output &&
-       ! grep "^Writing objects"
-'
-
-test_expect_success 'push --progress shows progress to non-tty' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       test_commit progress &&
-       git push --progress >output 2>&1 &&
-       grep "^To http" output &&
-       grep "^Writing objects" output
-'
-
-test_expect_success 'http push gives sane defaults to reflog' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       test_commit reflog-test &&
-       git push "$HTTPD_URL"/smart/test_repo.git &&
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
-               log -g -1 --format="%gn <%ge>" >actual &&
-       echo "anonymous <anonymous@http.127.0.0.1>" >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       test_commit custom-reflog-test &&
-       git push "$HTTPD_URL"/smart_custom_env/test_repo.git &&
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
-               log -g -1 --format="%gn <%ge>" >actual &&
-       echo "Custom User <custom@example.com>" >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success 'push over smart http with auth' '
-       cd "$ROOT_PATH/test_repo_clone" &&
-       echo push-auth-test >expect &&
-       test_commit push-auth-test &&
-       set_askpass user@host pass@host &&
-       git push "$HTTPD_URL"/auth/smart/test_repo.git &&
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
-               log -1 --format=%s >actual &&
-       expect_askpass both user@host &&
-       test_cmp expect actual
-'
-
-test_expect_success 'push to auth-only-for-push repo' '
-       cd "$ROOT_PATH/test_repo_clone" &&
-       echo push-half-auth >expect &&
-       test_commit push-half-auth &&
-       set_askpass user@host pass@host &&
-       git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
-               log -1 --format=%s >actual &&
-       expect_askpass both user@host &&
-       test_cmp expect actual
-'
-
-test_expect_success 'create repo without http.receivepack set' '
-       cd "$ROOT_PATH" &&
-       git init half-auth &&
-       (
-               cd half-auth &&
-               test_commit one
-       ) &&
-       git clone --bare half-auth "$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git"
-'
-
-test_expect_success 'clone via half-auth-complete does not need password' '
-       cd "$ROOT_PATH" &&
-       set_askpass wrong &&
-       git clone "$HTTPD_URL"/half-auth-complete/smart/half-auth.git \
-               half-auth-clone &&
-       expect_askpass none
-'
-
-test_expect_success 'push into half-auth-complete requires password' '
-       cd "$ROOT_PATH/half-auth-clone" &&
-       echo two >expect &&
-       test_commit two &&
-       set_askpass user@host pass@host &&
-       git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
-               log -1 --format=%s >actual &&
-       expect_askpass both user@host &&
-       test_cmp expect actual
-'
-
-stop_httpd
-test_done
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
new file mode 100755 (executable)
index 0000000..1a3a2b6
--- /dev/null
@@ -0,0 +1,175 @@
+#!/bin/sh
+
+test_description='test dumb fetching over http via static file'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       skip_all='skipping test, git built without http support'
+       test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+       git config push.default matching &&
+       echo content1 >file &&
+       git add file &&
+       git commit -m one
+       echo content2 >file &&
+       git add file &&
+       git commit -m two
+'
+
+test_expect_success 'create http-accessible bare repository with loose objects' '
+       cp -R .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        git config core.bare true &&
+        mkdir -p hooks &&
+        echo "exec git update-server-info" >hooks/post-update &&
+        chmod +x hooks/post-update &&
+        hooks/post-update
+       ) &&
+       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push public master:master
+'
+
+test_expect_success 'clone http repository' '
+       git clone $HTTPD_URL/dumb/repo.git clone-tmpl &&
+       cp -R clone-tmpl clone &&
+       test_cmp file clone/file
+'
+
+test_expect_success 'create password-protected repository' '
+       mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
+       cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+              "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git"
+'
+
+setup_askpass_helper
+
+test_expect_success 'cloning password-protected repository can fail' '
+       set_askpass wrong &&
+       test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail &&
+       expect_askpass both wrong
+'
+
+test_expect_success 'http auth can use user/pass in URL' '
+       set_askpass wrong &&
+       git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none &&
+       expect_askpass none
+'
+
+test_expect_success 'http auth can use just user in URL' '
+       set_askpass wrong pass@host &&
+       git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
+       expect_askpass pass user@host
+'
+
+test_expect_success 'http auth can request both user and pass' '
+       set_askpass user@host pass@host &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
+       expect_askpass both user@host
+'
+
+test_expect_success 'http auth respects credential helper config' '
+       test_config_global credential.helper "!f() {
+               cat >/dev/null
+               echo username=user@host
+               echo password=pass@host
+       }; f" &&
+       set_askpass wrong &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
+       expect_askpass none
+'
+
+test_expect_success 'http auth can get username from config' '
+       test_config_global "credential.$HTTPD_URL.username" user@host &&
+       set_askpass wrong pass@host &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
+       expect_askpass pass user@host
+'
+
+test_expect_success 'configured username does not override URL' '
+       test_config_global "credential.$HTTPD_URL.username" wrong &&
+       set_askpass wrong pass@host &&
+       git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
+       expect_askpass pass user@host
+'
+
+test_expect_success 'fetch changes via http' '
+       echo content >>file &&
+       git commit -a -m two &&
+       git push public &&
+       (cd clone && git pull) &&
+       test_cmp file clone/file
+'
+
+test_expect_success 'fetch changes via manual http-fetch' '
+       cp -R clone-tmpl clone2 &&
+
+       HEAD=$(git rev-parse --verify HEAD) &&
+       (cd clone2 &&
+        git http-fetch -a -w heads/master-new $HEAD $(git config remote.origin.url) &&
+        git checkout master-new &&
+        test $HEAD = $(git rev-parse --verify HEAD)) &&
+       test_cmp file clone2/file
+'
+
+test_expect_success 'http remote detects correct HEAD' '
+       git push public master:other &&
+       (cd clone &&
+        git remote set-head origin -d &&
+        git remote set-head origin -a &&
+        git symbolic-ref refs/remotes/origin/HEAD > output &&
+        echo refs/remotes/origin/master > expect &&
+        test_cmp expect output
+       )
+'
+
+test_expect_success 'fetch packed objects' '
+       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+        git --bare repack -a -d
+       ) &&
+       git clone $HTTPD_URL/dumb/repo_pack.git
+'
+
+test_expect_success 'fetch notices corrupt pack' '
+       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+        p=`ls objects/pack/pack-*.pack` &&
+        chmod u+w $p &&
+        printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+       ) &&
+       mkdir repo_bad1.git &&
+       (cd repo_bad1.git &&
+        git --bare init &&
+        test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad1.git &&
+        test 0 = `ls objects/pack/pack-*.pack | wc -l`
+       )
+'
+
+test_expect_success 'fetch notices corrupt idx' '
+       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+        p=`ls objects/pack/pack-*.idx` &&
+        chmod u+w $p &&
+        printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+       ) &&
+       mkdir repo_bad2.git &&
+       (cd repo_bad2.git &&
+        git --bare init &&
+        test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad2.git &&
+        test 0 = `ls objects/pack | wc -l`
+       )
+'
+
+test_expect_success 'did not use upload-pack service' '
+       grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
+       : >exp
+       test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
deleted file mode 100755 (executable)
index 8392624..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-#!/bin/sh
-
-test_description='test dumb fetching over http via static file'
-. ./test-lib.sh
-
-if test -n "$NO_CURL"; then
-       skip_all='skipping test, git built without http support'
-       test_done
-fi
-
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
-
-test_expect_success 'setup repository' '
-       git config push.default matching &&
-       echo content1 >file &&
-       git add file &&
-       git commit -m one
-       echo content2 >file &&
-       git add file &&
-       git commit -m two
-'
-
-test_expect_success 'create http-accessible bare repository with loose objects' '
-       cp -R .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-        git config core.bare true &&
-        mkdir -p hooks &&
-        echo "exec git update-server-info" >hooks/post-update &&
-        chmod +x hooks/post-update &&
-        hooks/post-update
-       ) &&
-       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       git push public master:master
-'
-
-test_expect_success 'clone http repository' '
-       git clone $HTTPD_URL/dumb/repo.git clone-tmpl &&
-       cp -R clone-tmpl clone &&
-       test_cmp file clone/file
-'
-
-test_expect_success 'create password-protected repository' '
-       mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
-       cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-              "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git"
-'
-
-setup_askpass_helper
-
-test_expect_success 'cloning password-protected repository can fail' '
-       set_askpass wrong &&
-       test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail &&
-       expect_askpass both wrong
-'
-
-test_expect_success 'http auth can use user/pass in URL' '
-       set_askpass wrong &&
-       git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none &&
-       expect_askpass none
-'
-
-test_expect_success 'http auth can use just user in URL' '
-       set_askpass wrong pass@host &&
-       git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
-       expect_askpass pass user@host
-'
-
-test_expect_success 'http auth can request both user and pass' '
-       set_askpass user@host pass@host &&
-       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
-       expect_askpass both user@host
-'
-
-test_expect_success 'http auth respects credential helper config' '
-       test_config_global credential.helper "!f() {
-               cat >/dev/null
-               echo username=user@host
-               echo password=pass@host
-       }; f" &&
-       set_askpass wrong &&
-       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
-       expect_askpass none
-'
-
-test_expect_success 'http auth can get username from config' '
-       test_config_global "credential.$HTTPD_URL.username" user@host &&
-       set_askpass wrong pass@host &&
-       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
-       expect_askpass pass user@host
-'
-
-test_expect_success 'configured username does not override URL' '
-       test_config_global "credential.$HTTPD_URL.username" wrong &&
-       set_askpass wrong pass@host &&
-       git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
-       expect_askpass pass user@host
-'
-
-test_expect_success 'fetch changes via http' '
-       echo content >>file &&
-       git commit -a -m two &&
-       git push public &&
-       (cd clone && git pull) &&
-       test_cmp file clone/file
-'
-
-test_expect_success 'fetch changes via manual http-fetch' '
-       cp -R clone-tmpl clone2 &&
-
-       HEAD=$(git rev-parse --verify HEAD) &&
-       (cd clone2 &&
-        git http-fetch -a -w heads/master-new $HEAD $(git config remote.origin.url) &&
-        git checkout master-new &&
-        test $HEAD = $(git rev-parse --verify HEAD)) &&
-       test_cmp file clone2/file
-'
-
-test_expect_success 'http remote detects correct HEAD' '
-       git push public master:other &&
-       (cd clone &&
-        git remote set-head origin -d &&
-        git remote set-head origin -a &&
-        git symbolic-ref refs/remotes/origin/HEAD > output &&
-        echo refs/remotes/origin/master > expect &&
-        test_cmp expect output
-       )
-'
-
-test_expect_success 'fetch packed objects' '
-       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
-        git --bare repack -a -d
-       ) &&
-       git clone $HTTPD_URL/dumb/repo_pack.git
-'
-
-test_expect_success 'fetch notices corrupt pack' '
-       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
-        p=`ls objects/pack/pack-*.pack` &&
-        chmod u+w $p &&
-        printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
-       ) &&
-       mkdir repo_bad1.git &&
-       (cd repo_bad1.git &&
-        git --bare init &&
-        test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad1.git &&
-        test 0 = `ls objects/pack/pack-*.pack | wc -l`
-       )
-'
-
-test_expect_success 'fetch notices corrupt idx' '
-       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
-        p=`ls objects/pack/pack-*.idx` &&
-        chmod u+w $p &&
-        printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
-       ) &&
-       mkdir repo_bad2.git &&
-       (cd repo_bad2.git &&
-        git --bare init &&
-        test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad2.git &&
-        test 0 = `ls objects/pack | wc -l`
-       )
-'
-
-test_expect_success 'did not use upload-pack service' '
-       grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
-       : >exp
-       test_cmp exp act
-'
-
-stop_httpd
-test_done
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
new file mode 100755 (executable)
index 0000000..e07eaf3
--- /dev/null
@@ -0,0 +1,252 @@
+#!/bin/sh
+
+test_description='test smart fetching over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       skip_all='skipping test, git built without http support'
+       test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+       git config push.default matching &&
+       echo content >file &&
+       git add file &&
+       git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        git --bare init
+       ) &&
+       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push public master:master
+'
+
+setup_askpass_helper
+
+cat >exp <<EOF
+> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+> Accept: */*
+> Accept-Encoding: gzip
+> Pragma: no-cache
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-advertisement
+> POST /smart/repo.git/git-upload-pack HTTP/1.1
+> Accept-Encoding: gzip
+> Content-Type: application/x-git-upload-pack-request
+> Accept: application/x-git-upload-pack-result
+> Content-Length: xxx
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-result
+EOF
+test_expect_success 'clone http repository' '
+       GIT_CURL_VERBOSE=1 git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
+       test_cmp file clone/file &&
+       tr '\''\015'\'' Q <err |
+       sed -e "
+               s/Q\$//
+               /^[*] /d
+               /^$/d
+               /^< $/d
+
+               /^[^><]/{
+                       s/^/> /
+               }
+
+               /^> User-Agent: /d
+               /^> Host: /d
+               /^> POST /,$ {
+                       /^> Accept: [*]\\/[*]/d
+               }
+               s/^> Content-Length: .*/> Content-Length: xxx/
+               /^> 00..want /d
+               /^> 00.*done/d
+
+               /^< Server: /d
+               /^< Expires: /d
+               /^< Date: /d
+               /^< Content-Length: /d
+               /^< Transfer-Encoding: /d
+       " >act &&
+       test_cmp exp act
+'
+
+test_expect_success 'fetch changes via http' '
+       echo content >>file &&
+       git commit -a -m two &&
+       git push public
+       (cd clone && git pull) &&
+       test_cmp file clone/file
+'
+
+cat >exp <<EOF
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'used upload-pack service' '
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+       test_cmp exp act
+'
+
+test_expect_success 'follow redirects (301)' '
+       git clone $HTTPD_URL/smart-redir-perm/repo.git --quiet repo-p
+'
+
+test_expect_success 'follow redirects (302)' '
+       git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
+'
+
+test_expect_success 'redirects re-root further requests' '
+       git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
+'
+
+test_expect_success 'clone from password-protected repository' '
+       echo two >expect &&
+       set_askpass user@host pass@host &&
+       git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
+       expect_askpass both user@host &&
+       git --git-dir=smart-auth log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone from auth-only-for-push repository' '
+       echo two >expect &&
+       set_askpass wrong &&
+       git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth &&
+       expect_askpass none &&
+       git --git-dir=smart-noauth log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone from auth-only-for-objects repository' '
+       echo two >expect &&
+       set_askpass user@host pass@host &&
+       git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
+       expect_askpass both user@host &&
+       git --git-dir=half-auth log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'no-op half-auth fetch does not require a password' '
+       set_askpass wrong &&
+       git --git-dir=half-auth fetch &&
+       expect_askpass none
+'
+
+test_expect_success 'redirects send auth to new location' '
+       set_askpass user@host pass@host &&
+       git -c credential.useHttpPath=true \
+         clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
+       expect_askpass both user@host auth/smart/repo.git
+'
+
+test_expect_success 'disable dumb http on server' '
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+               config http.getanyfile false
+'
+
+test_expect_success 'GIT_SMART_HTTP can disable smart http' '
+       (GIT_SMART_HTTP=0 &&
+        export GIT_SMART_HTTP &&
+        cd clone &&
+        test_must_fail git fetch)
+'
+
+test_expect_success 'invalid Content-Type rejected' '
+       test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual
+       grep "not valid:" actual
+'
+
+test_expect_success 'create namespaced refs' '
+       test_commit namespaced &&
+       git push public HEAD:refs/namespaces/ns/refs/heads/master &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+               symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/master
+'
+
+test_expect_success 'smart clone respects namespace' '
+       git clone "$HTTPD_URL/smart_namespace/repo.git" ns-smart &&
+       echo namespaced >expect &&
+       git --git-dir=ns-smart/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'dumb clone via http-backend respects namespace' '
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+               config http.getanyfile true &&
+       GIT_SMART_HTTP=0 git clone \
+               "$HTTPD_URL/smart_namespace/repo.git" ns-dumb &&
+       echo namespaced >expect &&
+       git --git-dir=ns-dumb/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+cat >cookies.txt <<EOF
+127.0.0.1      FALSE   /smart_cookies/ FALSE   0       othername       othervalue
+EOF
+cat >expect_cookies.txt <<EOF
+
+127.0.0.1      FALSE   /smart_cookies/ FALSE   0       othername       othervalue
+127.0.0.1      FALSE   /smart_cookies/repo.git/info/   FALSE   0       name    value
+EOF
+test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
+       git config http.cookiefile cookies.txt &&
+       git config http.savecookies true &&
+       git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
+       tail -3 cookies.txt > cookies_tail.txt
+       test_cmp expect_cookies.txt cookies_tail.txt
+'
+
+test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
+
+test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       for i in `test_seq 50000`
+       do
+               echo "commit refs/heads/too-many-refs"
+               echo "mark :$i"
+               echo "committer git <git@example.com> $i +0000"
+               echo "data 0"
+               echo "M 644 inline bla.txt"
+               echo "data 4"
+               echo "bla"
+               # make every commit dangling by always
+               # rewinding the branch after each commit
+               echo "reset refs/heads/too-many-refs"
+               echo "from :1"
+       done | git fast-import --export-marks=marks &&
+
+       # now assign tags to all the dangling commits we created above
+       tag=$(perl -e "print \"bla\" x 30") &&
+       sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+       )
+'
+
+test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' '
+       git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err &&
+       test_line_count = 0 err &&
+       (
+               cd too-many-refs &&
+               test $(git for-each-ref refs/tags | wc -l) = 50000
+       )
+'
+
+stop_httpd
+test_done
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
deleted file mode 100755 (executable)
index a124efe..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-#!/bin/sh
-
-test_description='test smart fetching over http via http-backend'
-. ./test-lib.sh
-
-if test -n "$NO_CURL"; then
-       skip_all='skipping test, git built without http support'
-       test_done
-fi
-
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5551'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
-
-test_expect_success 'setup repository' '
-       git config push.default matching &&
-       echo content >file &&
-       git add file &&
-       git commit -m one
-'
-
-test_expect_success 'create http-accessible bare repository' '
-       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-        git --bare init
-       ) &&
-       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       git push public master:master
-'
-
-setup_askpass_helper
-
-cat >exp <<EOF
-> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
-> Accept: */*
-> Accept-Encoding: gzip
-> Pragma: no-cache
-< HTTP/1.1 200 OK
-< Pragma: no-cache
-< Cache-Control: no-cache, max-age=0, must-revalidate
-< Content-Type: application/x-git-upload-pack-advertisement
-> POST /smart/repo.git/git-upload-pack HTTP/1.1
-> Accept-Encoding: gzip
-> Content-Type: application/x-git-upload-pack-request
-> Accept: application/x-git-upload-pack-result
-> Content-Length: xxx
-< HTTP/1.1 200 OK
-< Pragma: no-cache
-< Cache-Control: no-cache, max-age=0, must-revalidate
-< Content-Type: application/x-git-upload-pack-result
-EOF
-test_expect_success 'clone http repository' '
-       GIT_CURL_VERBOSE=1 git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
-       test_cmp file clone/file &&
-       tr '\''\015'\'' Q <err |
-       sed -e "
-               s/Q\$//
-               /^[*] /d
-               /^$/d
-               /^< $/d
-
-               /^[^><]/{
-                       s/^/> /
-               }
-
-               /^> User-Agent: /d
-               /^> Host: /d
-               /^> POST /,$ {
-                       /^> Accept: [*]\\/[*]/d
-               }
-               s/^> Content-Length: .*/> Content-Length: xxx/
-               /^> 00..want /d
-               /^> 00.*done/d
-
-               /^< Server: /d
-               /^< Expires: /d
-               /^< Date: /d
-               /^< Content-Length: /d
-               /^< Transfer-Encoding: /d
-       " >act &&
-       test_cmp exp act
-'
-
-test_expect_success 'fetch changes via http' '
-       echo content >>file &&
-       git commit -a -m two &&
-       git push public
-       (cd clone && git pull) &&
-       test_cmp file clone/file
-'
-
-cat >exp <<EOF
-GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-EOF
-test_expect_success 'used upload-pack service' '
-       sed -e "
-               s/^.* \"//
-               s/\"//
-               s/ [1-9][0-9]*\$//
-               s/^GET /GET  /
-       " >act <"$HTTPD_ROOT_PATH"/access.log &&
-       test_cmp exp act
-'
-
-test_expect_success 'follow redirects (301)' '
-       git clone $HTTPD_URL/smart-redir-perm/repo.git --quiet repo-p
-'
-
-test_expect_success 'follow redirects (302)' '
-       git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
-'
-
-test_expect_success 'redirects re-root further requests' '
-       git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
-'
-
-test_expect_success 'clone from password-protected repository' '
-       echo two >expect &&
-       set_askpass user@host pass@host &&
-       git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
-       expect_askpass both user@host &&
-       git --git-dir=smart-auth log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'clone from auth-only-for-push repository' '
-       echo two >expect &&
-       set_askpass wrong &&
-       git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth &&
-       expect_askpass none &&
-       git --git-dir=smart-noauth log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'clone from auth-only-for-objects repository' '
-       echo two >expect &&
-       set_askpass user@host pass@host &&
-       git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
-       expect_askpass both user@host &&
-       git --git-dir=half-auth log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'no-op half-auth fetch does not require a password' '
-       set_askpass wrong &&
-       git --git-dir=half-auth fetch &&
-       expect_askpass none
-'
-
-test_expect_success 'redirects send auth to new location' '
-       set_askpass user@host pass@host &&
-       git -c credential.useHttpPath=true \
-         clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
-       expect_askpass both user@host auth/smart/repo.git
-'
-
-test_expect_success 'disable dumb http on server' '
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-               config http.getanyfile false
-'
-
-test_expect_success 'GIT_SMART_HTTP can disable smart http' '
-       (GIT_SMART_HTTP=0 &&
-        export GIT_SMART_HTTP &&
-        cd clone &&
-        test_must_fail git fetch)
-'
-
-test_expect_success 'invalid Content-Type rejected' '
-       test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual
-       grep "not valid:" actual
-'
-
-test_expect_success 'create namespaced refs' '
-       test_commit namespaced &&
-       git push public HEAD:refs/namespaces/ns/refs/heads/master &&
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-               symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/master
-'
-
-test_expect_success 'smart clone respects namespace' '
-       git clone "$HTTPD_URL/smart_namespace/repo.git" ns-smart &&
-       echo namespaced >expect &&
-       git --git-dir=ns-smart/.git log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'dumb clone via http-backend respects namespace' '
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-               config http.getanyfile true &&
-       GIT_SMART_HTTP=0 git clone \
-               "$HTTPD_URL/smart_namespace/repo.git" ns-dumb &&
-       echo namespaced >expect &&
-       git --git-dir=ns-dumb/.git log -1 --format=%s >actual &&
-       test_cmp expect actual
-'
-
-cat >cookies.txt <<EOF
-127.0.0.1      FALSE   /smart_cookies/ FALSE   0       othername       othervalue
-EOF
-cat >expect_cookies.txt <<EOF
-
-127.0.0.1      FALSE   /smart_cookies/ FALSE   0       othername       othervalue
-127.0.0.1      FALSE   /smart_cookies/repo.git/info/   FALSE   0       name    value
-EOF
-test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
-       git config http.cookiefile cookies.txt &&
-       git config http.savecookies true &&
-       git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
-       tail -3 cookies.txt > cookies_tail.txt
-       test_cmp expect_cookies.txt cookies_tail.txt
-'
-
-test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
-
-test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
-       (
-       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       for i in `test_seq 50000`
-       do
-               echo "commit refs/heads/too-many-refs"
-               echo "mark :$i"
-               echo "committer git <git@example.com> $i +0000"
-               echo "data 0"
-               echo "M 644 inline bla.txt"
-               echo "data 4"
-               echo "bla"
-               # make every commit dangling by always
-               # rewinding the branch after each commit
-               echo "reset refs/heads/too-many-refs"
-               echo "from :1"
-       done | git fast-import --export-marks=marks &&
-
-       # now assign tags to all the dangling commits we created above
-       tag=$(perl -e "print \"bla\" x 30") &&
-       sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
-       )
-'
-
-test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' '
-       git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err &&
-       test_line_count = 0 err &&
-       (
-               cd too-many-refs &&
-               test $(git for-each-ref refs/tags | wc -l) = 50000
-       )
-'
-
-stop_httpd
-test_done
index b5d7fbc3815aed53ec50bdf7f5dbf2c796fed1fe..d23fb0238483520e77004a208f15db808941b4af 100755 (executable)
@@ -8,7 +8,6 @@ if test -n "$NO_CURL"; then
        test_done
 fi
 
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5561'}
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index e06146835cdda5b7264a34a24405aedd9f950006..6b163799510fa608fc6effa146accf295d0068f5 100755 (executable)
@@ -3,7 +3,6 @@
 test_description='test fetching over git protocol'
 . ./test-lib.sh
 
-LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-5570}
 . "$TEST_DIRECTORY"/lib-git-daemon.sh
 start_git_daemon
 
index a7c7ff5f4938c1fa7439e7c5a45321572bfc55fe..39fc3f6769be1c41d76c6acbfcb780b823afe7e7 100755 (executable)
@@ -69,7 +69,7 @@ test_expect_success 'tree_entry_interesting matches :(icase)bar with empty prefi
        test_cmp expect actual
 '
 
-test_expect_success 'match_pathspec_depth matches :(icase)bar' '
+test_expect_success 'match_pathspec matches :(icase)bar' '
        cat <<-EOF >expect &&
        BAR
        bAr
@@ -79,7 +79,7 @@ test_expect_success 'match_pathspec_depth matches :(icase)bar' '
        test_cmp expect actual
 '
 
-test_expect_success 'match_pathspec_depth matches :(icase)bar with prefix' '
+test_expect_success 'match_pathspec matches :(icase)bar with prefix' '
        cat <<-EOF >expect &&
        fOo/BAR
        fOo/bAr
@@ -89,7 +89,7 @@ test_expect_success 'match_pathspec_depth matches :(icase)bar with prefix' '
        test_cmp expect actual
 '
 
-test_expect_success 'match_pathspec_depth matches :(icase)bar with empty prefix' '
+test_expect_success 'match_pathspec matches :(icase)bar with empty prefix' '
        cat <<-EOF >expect &&
        bar
        fOo/BAR
diff --git a/t/t7101-reset-empty-subdirs.sh b/t/t7101-reset-empty-subdirs.sh
new file mode 100755 (executable)
index 0000000..96e163f
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='git reset should cull empty subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+    'creating initial files' \
+    'mkdir path0 &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+     git add path0/COPYING &&
+     git commit -m add -a'
+
+test_expect_success \
+    'creating second files' \
+    'mkdir path1 &&
+     mkdir path1/path2 &&
+     cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
+     git add path1/path2/COPYING &&
+     git add path1/COPYING &&
+     git add COPYING &&
+     git add path0/COPYING-TOO &&
+     git commit -m change -a'
+
+test_expect_success \
+    'resetting tree HEAD^' \
+    'git reset --hard HEAD^'
+
+test_expect_success \
+    'checking initial files exist after rewind' \
+    'test -d path0 &&
+     test -f path0/COPYING'
+
+test_expect_success \
+    'checking lack of path1/path2/COPYING' \
+    '! test -f path1/path2/COPYING'
+
+test_expect_success \
+    'checking lack of path1/COPYING' \
+    '! test -f path1/COPYING'
+
+test_expect_success \
+    'checking lack of COPYING' \
+    '! test -f COPYING'
+
+test_expect_success \
+    'checking checking lack of path1/COPYING-TOO' \
+    '! test -f path0/COPYING-TOO'
+
+test_expect_success \
+    'checking lack of path1/path2' \
+    '! test -d path1/path2'
+
+test_expect_success \
+    'checking lack of path1' \
+    '! test -d path1'
+
+test_done
diff --git a/t/t7101-reset.sh b/t/t7101-reset.sh
deleted file mode 100755 (executable)
index 96e163f..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Shawn Pearce
-#
-
-test_description='git reset should cull empty subdirs'
-. ./test-lib.sh
-
-test_expect_success \
-    'creating initial files' \
-    'mkdir path0 &&
-     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
-     git add path0/COPYING &&
-     git commit -m add -a'
-
-test_expect_success \
-    'creating second files' \
-    'mkdir path1 &&
-     mkdir path1/path2 &&
-     cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
-     cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
-     cp "$TEST_DIRECTORY"/../COPYING COPYING &&
-     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
-     git add path1/path2/COPYING &&
-     git add path1/COPYING &&
-     git add COPYING &&
-     git add path0/COPYING-TOO &&
-     git commit -m change -a'
-
-test_expect_success \
-    'resetting tree HEAD^' \
-    'git reset --hard HEAD^'
-
-test_expect_success \
-    'checking initial files exist after rewind' \
-    'test -d path0 &&
-     test -f path0/COPYING'
-
-test_expect_success \
-    'checking lack of path1/path2/COPYING' \
-    '! test -f path1/path2/COPYING'
-
-test_expect_success \
-    'checking lack of path1/COPYING' \
-    '! test -f path1/COPYING'
-
-test_expect_success \
-    'checking lack of COPYING' \
-    '! test -f COPYING'
-
-test_expect_success \
-    'checking checking lack of path1/COPYING-TOO' \
-    '! test -f path0/COPYING-TOO'
-
-test_expect_success \
-    'checking lack of path1/path2' \
-    '! test -d path1/path2'
-
-test_expect_success \
-    'checking lack of path1' \
-    '! test -d path1'
-
-test_done
index 8d4b50d1b5816d69ab165119773a7ffc0c360c0a..bc0846f4356e548cc72d3cf0c18ef00c21ede87d 100755 (executable)
@@ -535,4 +535,19 @@ test_expect_success 'reset with paths accepts tree' '
        git diff HEAD --exit-code
 '
 
+test_expect_success 'reset -N keeps removed files as intent-to-add' '
+       echo new-file >new-file &&
+       git add new-file &&
+       git reset -N HEAD &&
+
+       tree=$(git write-tree) &&
+       git ls-tree $tree new-file >actual &&
+       >expect &&
+       test_cmp expect actual &&
+
+       git diff --name-only >actual &&
+       echo new-file >expect &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh
new file mode 100755 (executable)
index 0000000..f136ee7
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='reset --hard unmerged'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       mkdir before later &&
+       >before/1 &&
+       >before/2 &&
+       >hello &&
+       >later/3 &&
+       git add before hello later &&
+       git commit -m world &&
+
+       H=$(git rev-parse :hello) &&
+       git rm --cached hello &&
+       echo "100644 $H 2       hello" | git update-index --index-info &&
+
+       rm -f hello &&
+       mkdir -p hello &&
+       >hello/world &&
+       test "$(git ls-files -o)" = hello/world
+
+'
+
+test_expect_success 'reset --hard should restore unmerged ones' '
+
+       git reset --hard &&
+       git ls-files --error-unmatch before/1 before/2 hello later/3 &&
+       test -f hello
+
+'
+
+test_expect_success 'reset --hard did not corrupt index nor cached-tree' '
+
+       T=$(git write-tree) &&
+       rm -f .git/index &&
+       git add before hello later &&
+       U=$(git write-tree) &&
+       test "$T" = "$U"
+
+'
+
+test_done
diff --git a/t/t7104-reset.sh b/t/t7104-reset.sh
deleted file mode 100755 (executable)
index f136ee7..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-
-test_description='reset --hard unmerged'
-
-. ./test-lib.sh
-
-test_expect_success setup '
-
-       mkdir before later &&
-       >before/1 &&
-       >before/2 &&
-       >hello &&
-       >later/3 &&
-       git add before hello later &&
-       git commit -m world &&
-
-       H=$(git rev-parse :hello) &&
-       git rm --cached hello &&
-       echo "100644 $H 2       hello" | git update-index --index-info &&
-
-       rm -f hello &&
-       mkdir -p hello &&
-       >hello/world &&
-       test "$(git ls-files -o)" = hello/world
-
-'
-
-test_expect_success 'reset --hard should restore unmerged ones' '
-
-       git reset --hard &&
-       git ls-files --error-unmatch before/1 before/2 hello later/3 &&
-       test -f hello
-
-'
-
-test_expect_success 'reset --hard did not corrupt index nor cached-tree' '
-
-       T=$(git write-tree) &&
-       rm -f .git/index &&
-       git add before hello later &&
-       U=$(git write-tree) &&
-       test "$T" = "$U"
-
-'
-
-test_done
index 0246e80b1af98fe213b06beb8de0a9fec464b9f2..28ca76384f6472b5d74564450ed122da9d49fe70 100755 (executable)
@@ -63,6 +63,9 @@ test_expect_success 'setup a submodule tree' '
         git submodule add ../none none &&
         test_tick &&
         git commit -m "none"
+       ) &&
+       (cd super &&
+        git tag initial-setup
        )
 '
 
@@ -703,7 +706,7 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re
        git clone super_update_r super_update_r2 &&
        (cd super_update_r2 &&
         git submodule update --init --recursive >actual &&
-        test_i18ngrep "Submodule path .submodule/subsubmodule.: checked out" actual &&
+        test_i18ngrep "Submodule path .submodule/subsubmodule.: .git reset --hard -q" actual &&
         (cd submodule/subsubmodule &&
          git log > ../../expected
         ) &&
@@ -775,4 +778,37 @@ test_expect_success 'submodule update --recursive drops module name before recur
        )
 '
 
+test_expect_success 'submodule update --checkout clones detached HEAD' '
+       git clone super super4 &&
+       echo "detached HEAD" >expected &&
+       (cd super4 &&
+        git reset --hard initial-setup &&
+        git submodule init submodule &&
+        git submodule update >> /tmp/log 2>&1 &&
+        (cd submodule &&
+         git symbolic-ref HEAD > ../../actual ||
+         echo "detached HEAD" > ../../actual
+        )
+       ) &&
+       test_cmp actual expected &&
+       rm -rf super4
+'
+
+test_expect_success 'submodule update --merge clones attached HEAD' '
+       git clone super super4 &&
+       echo "refs/heads/master" >expected &&
+       (cd super4 &&
+        git reset --hard initial-setup &&
+        git submodule init submodule &&
+        git config submodule.submodule.update merge &&
+        git submodule update --merge &&
+        (cd submodule &&
+         git symbolic-ref HEAD > ../../actual ||
+         echo "detached HEAD" > ../../actual
+        )
+       ) &&
+       test_cmp actual expected &&
+       rm -rf super4
+'
+
 test_done
index 1d3c56fe61fa995e9bfe2d3a1e4a9fef2c211387..5ddac1a9f740c03f1d94f75db2c6e54dd3391eda 100755 (executable)
@@ -5,6 +5,8 @@ test_description='signed commit tests'
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
 test_expect_success GPG 'create signed commits' '
+       test_when_finished "test_unconfig commit.gpgsign" &&
+
        echo 1 >file && git add file &&
        test_tick && git commit -S -m initial &&
        git tag initial &&
@@ -25,12 +27,27 @@ test_expect_success GPG 'create signed commits' '
        git tag fourth-unsigned &&
 
        test_tick && git commit --amend -S -m "fourth signed" &&
-       git tag fourth-signed
+       git tag fourth-signed &&
+
+       git config commit.gpgsign true &&
+       echo 5 >file && test_tick && git commit -a -m "fifth signed" &&
+       git tag fifth-signed &&
+
+       git config commit.gpgsign false &&
+       echo 6 >file && test_tick && git commit -a -m "sixth" &&
+       git tag sixth-unsigned &&
+
+       git config commit.gpgsign true &&
+       echo 7 >file && test_tick && git commit -a -m "seventh" --no-gpg-sign &&
+       git tag seventh-unsigned &&
+
+       test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
+       git tag seventh-signed
 '
 
 test_expect_success GPG 'show signatures' '
        (
-               for commit in initial second merge master
+               for commit in initial second merge fourth-signed fifth-signed sixth-signed master
                do
                        git show --pretty=short --show-signature $commit >actual &&
                        grep "Good signature from" actual || exit 1
@@ -39,7 +56,7 @@ test_expect_success GPG 'show signatures' '
                done
        ) &&
        (
-               for commit in merge^2 fourth-unsigned
+               for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
                do
                        git show --pretty=short --show-signature $commit >actual &&
                        grep "Good signature from" actual && exit 1
@@ -52,7 +69,7 @@ test_expect_success GPG 'show signatures' '
 test_expect_success GPG 'detect fudged signature' '
        git cat-file commit master >raw &&
 
-       sed -e "s/fourth signed/4th forged/" raw >forged1 &&
+       sed -e "s/seventh/7th forged/" raw >forged1 &&
        git hash-object -w -t commit forged1 >forged1.commit &&
        git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
        grep "BAD signature from" actual1 &&
index 830a4c3e9dffc7963dda03139be6c4e66fddf10f..f768c900abd1cddc6d69a1bdeae897374e7d0dad 100755 (executable)
@@ -38,6 +38,27 @@ test_expect_success 'merge c1 with c2' '
        test -f c2.c
 '
 
+test_expect_success 'fast-forward pull succeeds with "true" in pull.ff' '
+       git reset --hard c0 &&
+       test_config pull.ff true &&
+       git pull . c1 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse c1)"
+'
+
+test_expect_success 'fast-forward pull creates merge with "false" in pull.ff' '
+       git reset --hard c0 &&
+       test_config pull.ff false &&
+       git pull . c1 &&
+       test "$(git rev-parse HEAD^1)" = "$(git rev-parse c0)" &&
+       test "$(git rev-parse HEAD^2)" = "$(git rev-parse c1)"
+'
+
+test_expect_success 'pull prevents non-fast-forward with "only" in pull.ff' '
+       git reset --hard c1 &&
+       test_config pull.ff only &&
+       test_must_fail git pull . c3
+'
+
 test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '
        git reset --hard c1 &&
        git config pull.twohead ours &&
index aeae3ca7699b4c828f8ebe50624b5179fb5098b8..b333e3ff86aaaed41fa02b8c077848011907600e 100644 (file)
@@ -716,6 +716,64 @@ perl () {
        command "$PERL_PATH" "$@"
 }
 
+# Is the value one of the various ways to spell a boolean true/false?
+test_normalize_bool () {
+       git -c magic.variable="$1" config --bool magic.variable 2>/dev/null
+}
+
+# Given a variable $1, normalize the value of it to one of "true",
+# "false", or "auto" and store the result to it.
+#
+#     test_tristate GIT_TEST_HTTPD
+#
+# A variable set to an empty string is set to 'false'.
+# A variable set to 'false' or 'auto' keeps its value.
+# Anything else is set to 'true'.
+# An unset variable defaults to 'auto'.
+#
+# The last rule is to allow people to set the variable to an empty
+# string and export it to decline testing the particular feature
+# for versions both before and after this change.  We used to treat
+# both unset and empty variable as a signal for "do not test" and
+# took any non-empty string as "please test".
+
+test_tristate () {
+       if eval "test x\"\${$1+isset}\" = xisset"
+       then
+               # explicitly set
+               eval "
+                       case \"\$$1\" in
+                       '')     $1=false ;;
+                       auto)   ;;
+                       *)      $1=\$(test_normalize_bool \$$1 || echo true) ;;
+                       esac
+               "
+       else
+               eval "$1=auto"
+       fi
+}
+
+# Exit the test suite, either by skipping all remaining tests or by
+# exiting with an error. If "$1" is "auto", we then we assume we were
+# opportunistically trying to set up some tests and we skip. If it is
+# "true", then we report a failure.
+#
+# The error/skip message should be given by $2.
+#
+test_skip_or_die () {
+       case "$1" in
+       auto)
+               skip_all=$2
+               test_done
+               ;;
+       true)
+               error "$2"
+               ;;
+       *)
+               error "BUG: test tristate is '$1' (real error: $2)"
+       esac
+}
+
 # The following mingw_* functions obey POSIX shell syntax, but are actually
 # bash scripts, and are meant to be used only with bash on Windows.
 
diff --git a/test-hashmap.c b/test-hashmap.c
new file mode 100644 (file)
index 0000000..f5183fb
--- /dev/null
@@ -0,0 +1,255 @@
+#include "git-compat-util.h"
+#include "hashmap.h"
+
+struct test_entry
+{
+       struct hashmap_entry ent;
+       /* key and value as two \0-terminated strings */
+       char key[FLEX_ARRAY];
+};
+
+static const char *get_value(const struct test_entry *e)
+{
+       return e->key + strlen(e->key) + 1;
+}
+
+static int test_entry_cmp(const struct test_entry *e1,
+               const struct test_entry *e2, const char* key)
+{
+       return strcmp(e1->key, key ? key : e2->key);
+}
+
+static int test_entry_cmp_icase(const struct test_entry *e1,
+               const struct test_entry *e2, const char* key)
+{
+       return strcasecmp(e1->key, key ? key : e2->key);
+}
+
+static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
+               char *value, int vlen)
+{
+       struct test_entry *entry = malloc(sizeof(struct test_entry) + klen
+                       + vlen + 2);
+       hashmap_entry_init(entry, hash);
+       memcpy(entry->key, key, klen + 1);
+       memcpy(entry->key + klen + 1, value, vlen + 1);
+       return entry;
+}
+
+#define HASH_METHOD_FNV 0
+#define HASH_METHOD_I 1
+#define HASH_METHOD_IDIV10 2
+#define HASH_METHOD_0 3
+#define HASH_METHOD_X2 4
+#define TEST_SPARSE 8
+#define TEST_ADD 16
+#define TEST_SIZE 100000
+
+static unsigned int hash(unsigned int method, unsigned int i, const char *key)
+{
+       unsigned int hash;
+       switch (method & 3)
+       {
+       case HASH_METHOD_FNV:
+               hash = strhash(key);
+               break;
+       case HASH_METHOD_I:
+               hash = i;
+               break;
+       case HASH_METHOD_IDIV10:
+               hash = i / 10;
+               break;
+       case HASH_METHOD_0:
+               hash = 0;
+               break;
+       }
+
+       if (method & HASH_METHOD_X2)
+               hash = 2 * hash;
+       return hash;
+}
+
+/*
+ * Test performance of hashmap.[ch]
+ * Usage: time echo "perfhashmap method rounds" | test-hashmap
+ */
+static void perf_hashmap(unsigned int method, unsigned int rounds)
+{
+       struct hashmap map;
+       char buf[16];
+       struct test_entry **entries;
+       unsigned int *hashes;
+       unsigned int i, j;
+
+       entries = malloc(TEST_SIZE * sizeof(struct test_entry *));
+       hashes = malloc(TEST_SIZE * sizeof(int));
+       for (i = 0; i < TEST_SIZE; i++) {
+               snprintf(buf, sizeof(buf), "%i", i);
+               entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0);
+               hashes[i] = hash(method, i, entries[i]->key);
+       }
+
+       if (method & TEST_ADD) {
+               /* test adding to the map */
+               for (j = 0; j < rounds; j++) {
+                       hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
+
+                       /* add entries */
+                       for (i = 0; i < TEST_SIZE; i++) {
+                               hashmap_entry_init(entries[i], hashes[i]);
+                               hashmap_add(&map, entries[i]);
+                       }
+
+                       hashmap_free(&map, 0);
+               }
+       } else {
+               /* test map lookups */
+               hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
+
+               /* fill the map (sparsely if specified) */
+               j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
+               for (i = 0; i < j; i++) {
+                       hashmap_entry_init(entries[i], hashes[i]);
+                       hashmap_add(&map, entries[i]);
+               }
+
+               for (j = 0; j < rounds; j++) {
+                       for (i = 0; i < TEST_SIZE; i++) {
+                               struct hashmap_entry key;
+                               hashmap_entry_init(&key, hashes[i]);
+                               hashmap_get(&map, &key, entries[i]->key);
+                       }
+               }
+
+               hashmap_free(&map, 0);
+       }
+}
+
+#define DELIM " \t\r\n"
+
+/*
+ * Read stdin line by line and print result of commands to stdout:
+ *
+ * hash key -> strhash(key) memhash(key) strihash(key) memihash(key)
+ * put key value -> NULL / old value
+ * get key -> NULL / value
+ * remove key -> NULL / old value
+ * iterate -> key1 value1\nkey2 value2\n...
+ * size -> tablesize numentries
+ *
+ * perfhashmap method rounds -> test hashmap.[ch] performance
+ */
+int main(int argc, char *argv[])
+{
+       char line[1024];
+       struct hashmap map;
+       int icase;
+
+       /* init hash map */
+       icase = argc > 1 && !strcmp("ignorecase", argv[1]);
+       hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase
+                       : test_entry_cmp), 0);
+
+       /* process commands from stdin */
+       while (fgets(line, sizeof(line), stdin)) {
+               char *cmd, *p1 = NULL, *p2 = NULL;
+               int l1 = 0, l2 = 0, hash = 0;
+               struct test_entry *entry;
+
+               /* break line into command and up to two parameters */
+               cmd = strtok(line, DELIM);
+               /* ignore empty lines */
+               if (!cmd || *cmd == '#')
+                       continue;
+
+               p1 = strtok(NULL, DELIM);
+               if (p1) {
+                       l1 = strlen(p1);
+                       hash = icase ? strihash(p1) : strhash(p1);
+                       p2 = strtok(NULL, DELIM);
+                       if (p2)
+                               l2 = strlen(p2);
+               }
+
+               if (!strcmp("hash", cmd) && l1) {
+
+                       /* print results of different hash functions */
+                       printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1),
+                                       strihash(p1), memihash(p1, l1));
+
+               } else if (!strcmp("add", cmd) && l1 && l2) {
+
+                       /* create entry with key = p1, value = p2 */
+                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
+
+                       /* add to hashmap */
+                       hashmap_add(&map, entry);
+
+               } else if (!strcmp("put", cmd) && l1 && l2) {
+
+                       /* create entry with key = p1, value = p2 */
+                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
+
+                       /* add / replace entry */
+                       entry = hashmap_put(&map, entry);
+
+                       /* print and free replaced entry, if any */
+                       puts(entry ? get_value(entry) : "NULL");
+                       free(entry);
+
+               } else if (!strcmp("get", cmd) && l1) {
+
+                       /* setup static key */
+                       struct hashmap_entry key;
+                       hashmap_entry_init(&key, hash);
+
+                       /* lookup entry in hashmap */
+                       entry = hashmap_get(&map, &key, p1);
+
+                       /* print result */
+                       if (!entry)
+                               puts("NULL");
+                       while (entry) {
+                               puts(get_value(entry));
+                               entry = hashmap_get_next(&map, entry);
+                       }
+
+               } else if (!strcmp("remove", cmd) && l1) {
+
+                       /* setup static key */
+                       struct hashmap_entry key;
+                       hashmap_entry_init(&key, hash);
+
+                       /* remove entry from hashmap */
+                       entry = hashmap_remove(&map, &key, p1);
+
+                       /* print result and free entry*/
+                       puts(entry ? get_value(entry) : "NULL");
+                       free(entry);
+
+               } else if (!strcmp("iterate", cmd)) {
+
+                       struct hashmap_iter iter;
+                       hashmap_iter_init(&map, &iter);
+                       while ((entry = hashmap_iter_next(&iter)))
+                               printf("%s %s\n", entry->key, get_value(entry));
+
+               } else if (!strcmp("size", cmd)) {
+
+                       /* print table sizes */
+                       printf("%u %u\n", map.tablesize, map.size);
+
+               } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
+
+                       perf_hashmap(atoi(p1), atoi(p2));
+
+               } else {
+
+                       printf("Unknown command %s\n", cmd);
+
+               }
+       }
+
+       hashmap_free(&map, 1);
+       return 0;
+}
index 456660c7a29ef75030141721205f34134daea51c..11c3550177dbaab30596be2fc3c1633f2a0485f8 100644 (file)
@@ -294,14 +294,10 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
        unsigned long size1, size2;
        int retval;
 
-       tree1 = read_object_with_reference(old, tree_type, &size1, NULL);
-       if (!tree1)
-               die("unable to read source tree (%s)", sha1_to_hex(old));
-       tree2 = read_object_with_reference(new, tree_type, &size2, NULL);
-       if (!tree2)
-               die("unable to read destination tree (%s)", sha1_to_hex(new));
-       init_tree_desc(&t1, tree1, size1);
-       init_tree_desc(&t2, tree2, size2);
+       tree1 = fill_tree_descriptor(&t1, old);
+       tree2 = fill_tree_descriptor(&t2, new);
+       size1 = t1.size;
+       size2 = t2.size;
        retval = diff_tree(&t1, &t2, base, opt);
        if (!*base && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
                init_tree_desc(&t1, tree1, size1);
@@ -315,18 +311,5 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
 
 int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_options *opt)
 {
-       int retval;
-       void *tree;
-       unsigned long size;
-       struct tree_desc empty, real;
-
-       tree = read_object_with_reference(new, tree_type, &size, NULL);
-       if (!tree)
-               die("unable to read root tree (%s)", sha1_to_hex(new));
-       init_tree_desc(&real, tree, size);
-
-       init_tree_desc(&empty, "", 0);
-       retval = diff_tree(&empty, &real, base, opt);
-       free(tree);
-       return retval;
+       return diff_tree_sha1(NULL, new, base, opt);
 }
index 79dba1d0f4e729b5e62825d347c7e6a9b1455f1d..4dc86c7fe5f66b858b6bd7834a76b1dbbb63a339 100644 (file)
@@ -37,7 +37,7 @@ static void decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned
 
        /* Initialize the descriptor entry */
        desc->entry.path = path;
-       desc->entry.mode = mode;
+       desc->entry.mode = canon_mode(mode);
        desc->entry.sha1 = (const unsigned char *)(path + len);
 }
 
index ae04b6417de0ff06391cc7e2d98fcd1b7d5aa633..ae7fb3a824a960c6c03efae799683cd7e18d402c 100644 (file)
@@ -16,7 +16,7 @@ struct tree_desc {
 static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
 {
        *pathp = desc->entry.path;
-       *modep = canon_mode(desc->entry.mode);
+       *modep = desc->entry.mode;
        return desc->entry.sha1;
 }
 
index 164354dad7cbbaa7100f73256807680a75188021..0692ebe16e62fa4b31a20ec9c28cabb041236709 100644 (file)
@@ -105,12 +105,11 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
 static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
                         unsigned int set, unsigned int clear)
 {
-       clear |= CE_HASHED | CE_UNHASHED;
+       clear |= CE_HASHED;
 
        if (set & CE_REMOVE)
                set |= CE_WT_REMOVE;
 
-       ce->next = NULL;
        ce->ce_flags = (ce->ce_flags & ~clear) | set;
        add_index_entry(&o->result, ce,
                        ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
diff --git a/utf8.c b/utf8.c
index 0d20e0acb2b6fb2dd1d63abed676a036b2b7ec2f..a831d5089908df61574119b01a29a1991196f63a 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -5,8 +5,8 @@
 /* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
 
 struct interval {
-  int first;
-  int last;
+       ucs_char_t first;
+       ucs_char_t last;
 };
 
 size_t display_mode_esc_sequence_len(const char *s)
@@ -529,7 +529,7 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outs
        while (1) {
                size_t cnt = iconv(conv, &cp, &insz, &outpos, &outsz);
 
-               if (cnt == -1) {
+               if (cnt == (size_t) -1) {
                        size_t sofar;
                        if (errno != E2BIG) {
                                free(out);
index 4e5581005936a1e7832e3ab224cfe142ac777a63..a452407dad51940576db64b56d3a56cc7b364835 100644 (file)
@@ -510,7 +510,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
                struct wt_status_change_data *d;
                const struct cache_entry *ce = active_cache[i];
 
-               if (!ce_path_match(ce, &s->pathspec))
+               if (!ce_path_match(ce, &s->pathspec, NULL))
                        continue;
                it = string_list_insert(&s->change, ce->name);
                d = it->util;
@@ -552,7 +552,7 @@ static void wt_status_collect_untracked(struct wt_status *s)
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
                if (cache_name_is_other(ent->name, ent->len) &&
-                   match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
+                   dir_path_match(ent, &s->pathspec, 0, NULL))
                        string_list_insert(&s->untracked, ent->name);
                free(ent);
        }
@@ -560,7 +560,7 @@ static void wt_status_collect_untracked(struct wt_status *s)
        for (i = 0; i < dir.ignored_nr; i++) {
                struct dir_entry *ent = dir.ignored[i];
                if (cache_name_is_other(ent->name, ent->len) &&
-                   match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
+                   dir_path_match(ent, &s->pathspec, 0, NULL))
                        string_list_insert(&s->ignored, ent->name);
                free(ent);
        }