Code

Merge branch 'mn/maint-iconv-autoconf'
authorJunio C Hamano <gitster@pobox.com>
Sun, 21 Jun 2009 04:47:22 +0000 (21:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 21 Jun 2009 04:47:22 +0000 (21:47 -0700)
* mn/maint-iconv-autoconf:
  fix handling of iconv configuration options

233 files changed:
Documentation/RelNotes-1.6.4.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-apply.txt
Documentation/git-branch.txt
Documentation/git-check-ref-format.txt
Documentation/git-cvsexportcommit.txt
Documentation/git-difftool.txt
Documentation/git-mergetool.txt
Documentation/git-mktree.txt
Documentation/git-parse-remote.txt
Documentation/git-rebase.txt
Documentation/git-repack.txt
Documentation/git-rerere.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-show-branch.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git.txt
Documentation/gitmodules.txt
Documentation/merge-config.txt
Documentation/technical/api-parse-options.txt
GIT-VERSION-GEN
Makefile
RelNotes
archive.c
attr.c
base85.c
bisect.c
bisect.h
builtin-add.c
builtin-apply.c
builtin-archive.c
builtin-bisect--helper.c
builtin-blame.c
builtin-branch.c
builtin-cat-file.c
builtin-check-attr.c
builtin-checkout-index.c
builtin-checkout.c
builtin-clean.c
builtin-clone.c
builtin-commit.c
builtin-config.c
builtin-count-objects.c
builtin-describe.c
builtin-fast-export.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-fsck.c
builtin-gc.c
builtin-grep.c
builtin-help.c
builtin-log.c
builtin-ls-files.c
builtin-mailinfo.c
builtin-merge-base.c
builtin-merge-file.c
builtin-merge-recursive.c
builtin-merge.c
builtin-mktree.c [new file with mode: 0644]
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-pack-refs.c
builtin-prune.c
builtin-push.c
builtin-receive-pack.c
builtin-remote.c
builtin-reset.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-branch.c
builtin-symbolic-ref.c
builtin-tag.c
builtin-unpack-objects.c
builtin-update-ref.c
builtin-upload-archive.c
builtin.h
bundle.c
cache.h
compat/basename.c [new file with mode: 0644]
compat/mingw.c
compat/mingw.h
compat/mkstemps.c [new file with mode: 0644]
compat/nedmalloc/License.txt [new file with mode: 0644]
compat/nedmalloc/Readme.txt [new file with mode: 0644]
compat/nedmalloc/malloc.c.h [new file with mode: 0644]
compat/nedmalloc/nedmalloc.c [new file with mode: 0644]
compat/nedmalloc/nedmalloc.h [new file with mode: 0644]
compat/snprintf.c
compat/winansi.c
config.mak.in
configure.ac
connect.c
contrib/completion/git-completion.bash
contrib/fast-import/import-tars.perl
contrib/hooks/post-receive-email [changed mode: 0644->0755]
decorate.c
delta.h
diff.c
dir.c
git-am.sh
git-bisect.sh
git-compat-util.h
git-cvsexportcommit.perl
git-mergetool--lib.sh
git-parse-remote.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
gitk-git/gitk
gitk-git/po/de.po
gitweb/gitweb.perl
graph.c
grep.h
hash-object.c
http-push.c
http-walker.c
http.c
http.h
imap-send.c
index-pack.c
ll-merge.c
log-tree.c
merge-index.c
mktree.c [deleted file]
object.c
pack-check.c
parse-options.c
parse-options.h
patch-delta.c
path.c
perl/Git.pm
pretty.c
refs.c
refs.h
remote.c
remote.h
revision.c
revision.h
sha1_file.c
symlinks.c
t/lib-git-svn.sh
t/lib-httpd.sh
t/t0040-parse-options.sh
t/t1010-mktree.sh [new file with mode: 0755]
t/t3030-merge-recursive.sh
t/t3400-rebase.sh
t/t3700-add.sh
t/t3702-add-edit.sh [new file with mode: 0755]
t/t3900-i18n-commit.sh
t/t3900/EUCJP.txt [deleted file]
t/t3900/ISO-8859-1.txt [deleted file]
t/t3900/ISO8859-1.txt [new file with mode: 0644]
t/t3900/eucJP.txt [new file with mode: 0644]
t/t3901-i18n-patch.sh
t/t4013/diff.log_--decorate_--all
t/t4020-diff-external.sh
t/t4202-log.sh
t/t5100/rfc2047-samples.mbox
t/t5100/sample.mbox
t/t5500-fetch-pack.sh
t/t5520-pull.sh
t/t5540-http-push.sh
t/t5550-http-fetch.sh
t/t6030-bisect-porcelain.sh
t/t6040-tracking-info.sh
t/t7406-submodule-reference.sh [new file with mode: 0755]
t/t7406-submodule-update.sh [new file with mode: 0755]
t/t8005-blame-i18n.sh
t/t8005/cp1251.txt [deleted file]
t/t8005/iso8859-5.txt [new file with mode: 0644]
t/t9001-send-email.sh
t/t9100-git-svn-basic.sh
t/t9101-git-svn-props.sh
t/t9102-git-svn-deep-rmdir.sh
t/t9103-git-svn-tracked-directory-removed.sh
t/t9104-git-svn-follow-parent.sh
t/t9105-git-svn-commit-diff.sh
t/t9106-git-svn-commit-diff-clobber.sh
t/t9107-git-svn-migrate.sh
t/t9108-git-svn-glob.sh
t/t9109-git-svn-multi-glob.sh
t/t9113-git-svn-dcommit-new-file.sh
t/t9114-git-svn-dcommit-merge.sh
t/t9116-git-svn-log.sh
t/t9117-git-svn-init-clone.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9119-git-svn-info.sh
t/t9120-git-svn-clone-with-percent-escapes.sh
t/t9122-git-svn-author.sh
t/t9123-git-svn-rebuild-with-rewriteroot.sh
t/t9124-git-svn-dcommit-auto-props.sh
t/t9125-git-svn-multi-glob-branch-names.sh
t/t9127-git-svn-partial-rebuild.sh
t/t9128-git-svn-cmd-branch.sh
t/t9129-git-svn-i18n-commitencoding.sh
t/t9130-git-svn-authors-file.sh
t/t9133-git-svn-nested-git-repo.sh
t/t9134-git-svn-ignore-paths.sh
t/t9137-git-svn-dcommit-clobber-series.sh
t/t9138-git-svn-authors-prog.sh [new file with mode: 0755]
t/t9139-git-svn-non-utf8-commitencoding.sh [new file with mode: 0755]
t/t9200-git-cvsexportcommit.sh
t/t9301-fast-export.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9700-perl-git.sh
t/t9700/test.pl
t/test-lib.sh
templates/hooks--pre-commit.sample
templates/hooks--update.sample
test-chmtime.c
test-parse-options.c
transport.c
transport.h
userdiff.c
utf8.c

diff --git a/Documentation/RelNotes-1.6.4.txt b/Documentation/RelNotes-1.6.4.txt
new file mode 100644 (file)
index 0000000..af68297
--- /dev/null
@@ -0,0 +1,93 @@
+GIT v1.6.4 Release Notes
+========================
+
+With the next major release, "git push" into a branch that is
+currently checked out will be refused by default.  You can choose
+what should happen upon such a push by setting the configuration
+variable receive.denyCurrentBranch in the receiving repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing.  Please refer to:
+
+  http://git.or.cz/gitwiki/GitFaq#non-bare
+  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the reason why this change is needed and the
+transition plan.
+
+For a similar reason, "git push $there :$killed" to delete the branch
+$killed in a remote repository $there, if $killed branch is the current
+branch pointed at by its HEAD, gets a large warning.  You can choose what
+should happen upon such a push by setting the configuration variable
+receive.denyDeleteCurrent in the receiving repository.
+
+When the user does not tell "git push" what to push, it has always
+pushed matching refs.  For some people it is unexpected, and a new
+configuration variable push.default has been introduced to allow
+changing a different default behaviour.  To advertise the new feature,
+a big warning is issued if this is not configured and a git push without
+arguments is attempted.
+
+
+Updates since v1.6.3
+--------------------
+
+(subsystems)
+
+ * gitweb Perl style clean-up.
+
+ * git-svn updates, including a new --authors-prog option to map author
+   names by invoking an external program.
+
+(portability)
+
+ * We feed iconv with "UTF-8" instead of "utf8"; the former is
+   understood more widely.
+
+(performance)
+
+(usability, bells and whistles)
+
+ * "git add --edit" lets users edit the whole patch text to fine-tune what
+   is added to the index.
+
+ * "git log --graph" draws graphs more compactly by using horizonal lines
+   when able.
+
+ * "git log --decorate" shows shorter refnames by stripping well-known
+   refs/* prefix.
+
+ * "git send-email" understands quoted aliases in .mailrc files (might
+   have to be backported to 1.6.3.X).
+
+ * "git send-email" can fetch the sender address from the configuration
+   variable "sendmail.from" (and "sendmail.<identity>.from").
+
+ * "git show-branch" can color its output.
+
+ * "add" and "update" subcommands to "git submodule" learned --reference
+   option to use local clone with references.
+
+(developers)
+
+ * A major part of the "git bisect" wrapper has moved to C.
+
+Fixes since v1.6.3
+------------------
+
+All of the fixes in v1.6.3.X maintenance series are included in this
+release, unless otherwise noted.
+
+Here are fixes that this release has, but have not been backported to
+v1.6.3.X series.
+
+ * The way Git.pm sets up a Repository object was not friendly to callers
+   that chdir around.  It now internally records the repository location
+   as an absolute path when autodetected.
+
+---
+exec >/var/tmp/1
+echo O=$(git describe master)
+O=v1.6.3.1-168-g23807fa
+git shortlog --no-merges $O..master ^maint
index 5dcad94f841c395beb21961ebdacd341d76b25c9..3a86d1f8f05317717d718a6db7c5e0f9acb47d6b 100644 (file)
@@ -438,6 +438,11 @@ On some file system/operating system combinations, this is unreliable.
 Set this config setting to 'rename' there; However, This will remove the
 check that makes sure that existing object files will not get overwritten.
 
+add.ignore-errors::
+       Tells 'git-add' to continue adding files when some files cannot be
+       added due to indexing errors. Equivalent to the '--ignore-errors'
+       option of linkgit:git-add[1].
+
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
@@ -604,6 +609,12 @@ color.pager::
        A boolean to enable/disable colored output when the pager is in
        use (default is true).
 
+color.showbranch::
+       A boolean to enable/disable color in the output of
+       linkgit:git-show-branch[1]. May be set to `always`,
+       `false` (or `never`) or `auto` (or `true`), in which case colors are used
+       only when the output is to a terminal. Defaults to false.
+
 color.status::
        A boolean to enable/disable color in the output of
        linkgit:git-status[1]. May be set to `always`,
index d938b422893067feb1a430edb34fb51ef7db6d85..ab1943c71243df245f84ede7a69e4c666a8d8834 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
-         [--all | [--update | -u]] [--intent-to-add | -N]
+         [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
          [--refresh] [--ignore-errors] [--] <filepattern>...
 
 DESCRIPTION
@@ -76,6 +76,15 @@ OPTIONS
        bypassed and the 'patch' subcommand is invoked using each of
        the specified filepatterns before exiting.
 
+-e, \--edit::
+       Open the diff vs. the index in an editor and let the user
+       edit it.  After the editor was closed, adjust the hunk headers
+       and apply the patch to the index.
++
+*NOTE*: Obviously, if you change anything else than the first character
+on lines beginning with a space or a minus, the patch will no longer
+apply.
+
 -u::
 --update::
        Update only files that git already knows about, staging modified
index 9e5baa277731adf14e47fda8c6b13a076e0873db..735374d7df7e8d0e967dce7750f41aabff9a095f 100644 (file)
@@ -3,7 +3,7 @@ git-apply(1)
 
 NAME
 ----
-git-apply - Apply a patch on a git index file and a working tree
+git-apply - Apply a patch on a git index file and/or a working tree
 
 
 SYNOPSIS
index cbd427587188d81726445183f772f02982736e6a..ae201deb7af44c61e793ab408e916c5b6520800d 100644 (file)
@@ -111,6 +111,7 @@ OPTIONS
 --no-abbrev::
        Display the full sha1s in the output listing rather than abbreviating them.
 
+-t::
 --track::
        When creating a new branch, set up configuration to mark the
        start-point branch as "upstream" from the new branch. This
index c1ce26884e73f1599602472ba5032988486cc465..0b7982ea76633e45b8d3073cd275ea74a757c0eb 100644 (file)
@@ -25,6 +25,10 @@ imposes the following rules on how references are named:
   grouping, but no slash-separated component can begin with a
   dot `.`.
 
+. They must contain at least one `/`. This enforces the presence of a
+  category like `heads/`, `tags/` etc. but the actual names are not
+  restricted.
+
 . They cannot have two consecutive dots `..` anywhere.
 
 . They cannot have ASCII control characters (i.e. bytes whose
@@ -38,6 +42,8 @@ imposes the following rules on how references are named:
 
 . They cannot contain a sequence `@{`.
 
+- They cannot contain a `\\`.
+
 These rules make it easy for shell script based tools to parse
 reference names, pathname expansion by the shell when a reference name is used
 unquoted (by mistake), and also avoids ambiguities in certain
index 2da8588f4fd6edb842a9824181165b3f043ec87b..abaaf273bb3f39fef065781d89592fd7203ea620 100644 (file)
@@ -63,6 +63,10 @@ OPTIONS
 -u::
        Update affected files from CVS repository before attempting export.
 
+-k::
+       Reverse CVS keyword expansion (e.g. $Revision: 1.2.3.4$
+       becomes $Revision$) in working CVS checkout before applying patch.
+
 -w::
        Specify the location of the CVS checkout to use for the export. This
        option does not require GIT_DIR to be set before execution if the
index 15b247bab4195286e55447388308473af4e2d001..96a6c51a4b83207fa23fc8f09cb02f57b1085ea4 100644 (file)
@@ -31,7 +31,7 @@ OPTIONS
        Use the diff tool specified by <tool>.
        Valid merge tools are:
        kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff,
-       ecmerge, diffuse and opendiff
+       ecmerge, diffuse, opendiff and araxis.
 +
 If a diff tool is not specified, 'git-difftool'
 will use the configuration variable `diff.tool`.  If the
index ff9700d17a333f50d6509bc14a0bd81fad876d3b..68ed6c095664b8f06635ba7eaa85b85086b24280 100644 (file)
@@ -27,7 +27,7 @@ OPTIONS
        Use the merge resolution program specified by <tool>.
        Valid merge tools are:
        kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge,
-       diffuse, tortoisemerge and opendiff
+       diffuse, tortoisemerge, opendiff and araxis.
 +
 If a merge resolution program is not specified, 'git-mergetool'
 will use the configuration variable `merge.tool`.  If the
index af19f06ed738bdecc7ab9a72a5c9a216b816f4c2..81e3326772d94464708cc2037715e1e62eae5f11 100644 (file)
@@ -8,12 +8,13 @@ git-mktree - Build a tree-object from ls-tree formatted text
 
 SYNOPSIS
 --------
-'git mktree' [-z]
+'git mktree' [-z] [--missing] [--batch]
 
 DESCRIPTION
 -----------
-Reads standard input in non-recursive `ls-tree` output format,
-and creates a tree object.  The object name of the tree object
+Reads standard input in non-recursive `ls-tree` output format, and creates
+a tree object.  The order of the tree entries is normalised by mktree so
+pre-sorting the input is not required.  The object name of the tree object
 built is written to the standard output.
 
 OPTIONS
@@ -21,6 +22,18 @@ OPTIONS
 -z::
        Read the NUL-terminated `ls-tree -z` output instead.
 
+--missing::
+       Allow missing objects.  The default behaviour (without this option)
+       is to verify that each tree entry's sha1 identifies an existing
+       object.  This option has no effect on the treatment of gitlink entries
+       (aka "submodules") which are always allowed to be missing.
+
+--batch::
+       Allow building of more than one tree object before exiting.  Each
+       tree is separated by as single blank line. The final new-line is
+       optional.  Note - if the '-z' option is used, lines are terminated
+       with NUL.
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>
index cd43069874d59504627211e011250a3554aeee5a..39d9daa7e00b97ddcca8f3d25d7376234c57c246 100644 (file)
@@ -17,26 +17,6 @@ routines to parse files under $GIT_DIR/remotes/ and
 $GIT_DIR/branches/ and configuration variables that are related
 to fetching, pulling and pushing.
 
-The primary entry points are:
-
-get_remote_refs_for_fetch::
-       Given the list of user-supplied `<repo> <refspec>...`,
-       return the list of refs to fetch after canonicalizing
-       them into `$GIT_DIR` relative paths
-       (e.g. `refs/heads/foo`).  When `<refspec>...` is empty
-       the returned list of refs consists of the defaults
-       for the given `<repo>`, if specified in
-       `$GIT_DIR/remotes/`, `$GIT_DIR/branches/`, or `remote.*.fetch`
-       configuration.
-
-get_remote_refs_for_push::
-       Given the list of user-supplied `<repo> <refspec>...`,
-       return the list of refs to push in a form suitable to be
-       fed to the 'git-send-pack' command.  When `<refspec>...`
-       is empty the returned list of refs consists of the
-       defaults for the given `<repo>`, if specified in
-       `$GIT_DIR/remotes/`.
-
 Author
 ------
 Written by Junio C Hamano.
index 3d5a066c31675e502eb027dde824d1966c9c0f09..26f3b7b2b0daad0affb36e6514156520a693b164 100644 (file)
@@ -231,8 +231,7 @@ OPTIONS
 
 -s <strategy>::
 --strategy=<strategy>::
-       Use the given merge strategy; can be supplied more than
-       once to specify them in the order they should be tried.
+       Use the given merge strategy.
        If there is no `-s` option, a built-in list of strategies
        is used instead ('git-merge-recursive' when merging a single
        head, 'git-merge-octopus' otherwise).  This implies --merge.
index aaa88526291a26db55c7ebb0833faefda9c7e5a4..c9257a10c956432fdd2e456c7f910cc2996c1534 100644 (file)
@@ -31,11 +31,14 @@ OPTIONS
        Instead of incrementally packing the unpacked objects,
        pack everything referenced into a single pack.
        Especially useful when packing a repository that is used
-       for private development and there is no need to worry
-       about people fetching via dumb protocols from it.  Use
+       for private development. Use
        with '-d'.  This will clean up the objects that `git prune`
        leaves behind, but `git fsck --full` shows as
        dangling.
++
+Note that users fetching over dumb protocols will have to fetch the
+whole new pack in order to get any contained object, no matter how many
+other objects in that pack they already have locally.
 
 -A::
        Same as `-a`, unless '-d' is used.  Then any unreachable
index 64715c17da6c313a1cec4c353300eb0faee2313b..a53c3cd35b8d6c8be27d90f64013b377d205f201 100644 (file)
@@ -12,15 +12,15 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-In a workflow that employs relatively long lived topic branches,
-the developer sometimes needs to resolve the same conflict over
+In a workflow employing relatively long lived topic branches,
+the developer sometimes needs to resolve the same conflicts over
 and over again until the topic branches are done (either merged
 to the "release" branch, or sent out and accepted upstream).
 
-This command helps this process by recording conflicted
-automerge results and corresponding hand-resolve results on the
-initial manual merge, and later by noticing the same automerge
-results and applying the previously recorded hand resolution.
+This command assists the developer in this process by recording
+conflicted automerge results and corresponding hand resolve results
+on the initial manual merge, and applying previously recorded
+hand resolutions to their corresponding automerge results.
 
 [NOTE]
 You need to set the configuration variable rerere.enabled to
@@ -54,18 +54,18 @@ for resolutions.
 
 'gc'::
 
-This command is used to prune records of conflicted merge that
-occurred long time ago.  By default, conflicts older than 15
-days that you have not recorded their resolution, and conflicts
-older than 60 days, are pruned.  These are controlled with
+This prunes records of conflicted merges that
+occurred a long time ago.  By default, unresolved conflicts older
+than 15 days and resolved conflicts older than 60
+days are pruned.  These defaults are controlled via the
 `gc.rerereunresolved` and `gc.rerereresolved` configuration
-variables.
+variables respectively.
 
 
 DISCUSSION
 ----------
 
-When your topic branch modifies overlapping area that your
+When your topic branch modifies an overlapping area that your
 master branch (or upstream) touched since your topic branch
 forked from it, you may want to test it with the latest master,
 even before your topic branch is ready to be pushed upstream:
@@ -140,9 +140,9 @@ top of the tip before the test merge:
 This would leave only one merge commit when your topic branch is
 finally ready and merged into the master branch.  This merge
 would require you to resolve the conflict, introduced by the
-commits marked with `*`.  However, often this conflict is the
+commits marked with `*`.  However, this conflict is often the
 same conflict you resolved when you created the test merge you
-blew away.  'git-rerere' command helps you to resolve this final
+blew away.  'git-rerere' helps you resolve this final
 conflicted merge using the information from your earlier hand
 resolve.
 
@@ -150,33 +150,32 @@ Running the 'git-rerere' command immediately after a conflicted
 automerge records the conflicted working tree files, with the
 usual conflict markers `<<<<<<<`, `=======`, and `>>>>>>>` in
 them.  Later, after you are done resolving the conflicts,
-running 'git-rerere' again records the resolved state of these
+running 'git-rerere' again will record the resolved state of these
 files.  Suppose you did this when you created the test merge of
 master into the topic branch.
 
-Next time, running 'git-rerere' after seeing a conflicted
-automerge, if the conflict is the same as the earlier one
-recorded, it is noticed and a three-way merge between the
+Next time, after seeing the same conflicted automerge,
+running 'git-rerere' will perform a three-way merge between the
 earlier conflicted automerge, the earlier manual resolution, and
-the current conflicted automerge is performed by the command.
+the current conflicted automerge.
 If this three-way merge resolves cleanly, the result is written
-out to your working tree file, so you would not have to manually
+out to your working tree file, so you do not have to manually
 resolve it.  Note that 'git-rerere' leaves the index file alone,
 so you still need to do the final sanity checks with `git diff`
 (or `git diff -c`) and 'git-add' when you are satisfied.
 
 As a convenience measure, 'git-merge' automatically invokes
-'git-rerere' when it exits with a failed automerge, which
-records it if it is a new conflict, or reuses the earlier hand
+'git-rerere' upon exiting with a failed automerge and 'git-rerere'
+records the hand resolve when it is a new conflict, or reuses the earlier hand
 resolve when it is not.  'git-commit' also invokes 'git-rerere'
-when recording a merge result.  What this means is that you do
-not have to do anything special yourself (Note: you still have
-to set the config variable rerere.enabled to enable this command).
+when committing a merge result.  What this means is that you do
+not have to do anything special yourself (besides enabling
+the rerere.enabled config variable).
 
-In our example, when you did the test merge, the manual
+In our example, when you do the test merge, the manual
 resolution is recorded, and it will be reused when you do the
-actual merge later with updated master and topic branch, as long
-as the earlier resolution is still applicable.
+actual merge later with the updated master and topic branch, as long
+as the recorded resolution is still applicable.
 
 The information 'git-rerere' records is also used when running
 'git-rebase'.  After blowing away the test merge and continuing
@@ -194,11 +193,11 @@ development on the topic branch:
     o---o---o---*---o---o---o---o   master
 ------------
 
-you could run `git rebase master topic`, to keep yourself
-up-to-date even before your topic is ready to be sent upstream.
-This would result in falling back to three-way merge, and it
-would conflict the same way the test merge you resolved earlier.
-'git-rerere' is run by 'git-rebase' to help you resolve this
+you could run `git rebase master topic`, to bring yourself
+up-to-date before your topic is ready to be sent upstream.
+This would result in falling back to three-way merge, and it
+would conflict the same way as the test merge you resolved earlier.
+'git-rerere' will be run by 'git-rebase' to help you resolve this
 conflict.
 
 
index 52c353e674761bf4897484a261c702e5cc02f18a..4bbdd056da4ca5a3032b53afac820b86c74a54e4 100644 (file)
@@ -30,6 +30,11 @@ OPTIONS
        Only meaningful in `--parseopt` mode. Tells the option parser to echo
        out the first `--` met instead of skipping it.
 
+--sq-quote::
+       Use 'git-rev-parse' in shell quoting mode (see SQ-QUOTE
+       section below). In contrast to the `--sq` option below, this
+       mode does only quoting. Nothing else is done to command input.
+
 --revs-only::
        Do not output flags and parameters not meant for
        'git-rev-list' command.
@@ -64,7 +69,8 @@ OPTIONS
        properly quoted for consumption by shell.  Useful when
        you expect your parameter to contain whitespaces and
        newlines (e.g. when using pickaxe `-S` with
-       'git-diff-\*').
+       'git-diff-\*'). In contrast to the `--sq-quote` option,
+       the command input is still interpreted as usual.
 
 --not::
        When showing object names, prefix them with '{caret}' and
@@ -406,6 +412,33 @@ C?        option C with an optional argument"
 eval `echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?`
 ------------
 
+SQ-QUOTE
+--------
+
+In `--sq-quote` mode, 'git-rev-parse' echoes on the standard output a
+single line suitable for `sh(1)` `eval`. This line is made by
+normalizing the arguments following `--sq-quote`. Nothing other than
+quoting the arguments is done.
+
+If you want command input to still be interpreted as usual by
+'git-rev-parse' before the output is shell quoted, see the `--sq`
+option.
+
+Example
+~~~~~~~
+
+------------
+$ cat >your-git-script.sh <<\EOF
+#!/bin/sh
+args=$(git rev-parse --sq-quote "$@")   # quote user-supplied arguments
+command="git frotz -n24 $args"          # and use it inside a handcrafted
+                                       # command line
+eval "$command"
+EOF
+
+$ sh your-git-script.sh "a b'c"
+------------
+
 EXAMPLES
 --------
 
index 794224b1b3431655aa9e9683c9d938e35f7a3ea4..fbde2d3be523443c8388f9bb43cc0d5682658f8f 100644 (file)
@@ -14,6 +14,10 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Takes the patches given on the command line and emails them out.
+Patches can be specified as files, directories (which will send all
+files in the directory), or directly as a revision list.  In the
+last case, any format accepted by linkgit:git-format-patch[1] can
+be passed to git send-email.
 
 The header of the email is configurable by command line options.  If not
 specified on the command line, the user will be prompted with a ReadLine
@@ -39,6 +43,10 @@ OPTIONS
 Composing
 ~~~~~~~~~
 
+--annotate::
+       Review and edit each patch you're about to send. See the
+       CONFIGURATION section for 'sendemail.multiedit'.
+
 --bcc=<address>::
        Specify a "Bcc:" value for each email. Default is the value of
        'sendemail.bcc'.
@@ -51,11 +59,6 @@ The --bcc option must be repeated for each user you want on the bcc list.
 +
 The --cc option must be repeated for each user you want on the cc list.
 
---annotate::
-       Review each patch you're about to send in an editor. The setting
-       'sendemail.multiedit' defines if this will spawn one editor per patch
-       or one for all of them at once.
-
 --compose::
        Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
        introductory message for the patch series.
@@ -67,11 +70,16 @@ In-Reply-To headers specified in the message. If the body of the message
 and In-Reply-To headers will be used unless they are removed.
 +
 Missing From or In-Reply-To headers will be prompted for.
++
+See the CONFIGURATION section for 'sendemail.multiedit'.
 
 --from=<address>::
-       Specify the sender of the emails.  This will default to
-       the value GIT_COMMITTER_IDENT, as returned by "git var -l".
-       The user will still be prompted to confirm this entry.
+       Specify the sender of the emails.  If not specified on the command line,
+       the value of the 'sendemail.from' configuration option is used.  If
+       neither the command line option nor 'sendemail.from' are set, then the
+       user will be prompted for the value.  The default for the prompt will be
+       the value of GIT_AUTHOR_IDENT, or GIT_COMMITTER_IDENT if that is not
+       set, as returned by "git var -l".
 
 --in-reply-to=<identifier>::
        Specify the contents of the first In-Reply-To header.
@@ -135,7 +143,9 @@ user is prompted for a password while the input is masked for privacy.
 --smtp-server-port=<port>::
        Specifies a port different from the default port (SMTP
        servers typically listen to smtp port 25 and ssmtp port
-       465). This can be set with 'sendemail.smtpserverport'.
+       465); symbolic port names (e.g. "submission" instead of 465)
+       are also accepted. The port can also be set with the
+       'sendemail.smtpserverport' configuration variable.
 
 --smtp-ssl::
        Legacy alias for '--smtp-encryption ssl'.
@@ -155,7 +165,7 @@ Automating
        Output of this command must be single email address per line.
        Default is the value of 'sendemail.cccmd' configuration value.
 
---[no-]chain-reply-to=<identifier>::
+--[no-]chain-reply-to::
        If this is set, each email will be sent as a reply to the previous
        email sent.  If disabled with "--no-chain-reply-to", all emails after
        the first will be sent as replies to the first email sent.  When using
@@ -183,12 +193,12 @@ Automating
 - 'self' will avoid including the sender
 - 'cc' will avoid including anyone mentioned in Cc lines in the patch header
   except for self (use 'self' for that).
-- 'ccbody' will avoid including anyone mentioned in Cc lines in the
+- 'bodycc' will avoid including anyone mentioned in Cc lines in the
   patch body (commit message) except for self (use 'self' for that).
 - 'sob' will avoid including anyone mentioned in Signed-off-by lines except
    for self (use 'self' for that).
 - 'cccmd' will avoid running the --cc-cmd.
-- 'body' is equivalent to 'sob' + 'ccbody'
+- 'body' is equivalent to 'sob' + 'bodycc'
 - 'all' will suppress all auto cc values.
 --
 +
@@ -204,7 +214,8 @@ specified, as well as 'body' if --no-signed-off-cc is specified.
 --[no-]thread::
        If this is set, the In-Reply-To header will be set on each email sent.
        If disabled with "--no-thread", no emails will have the In-Reply-To
-       header set. Default is the value of the 'sendemail.thread' configuration
+       header set, unless specified with --in-reply-to.
+       Default is the value of the 'sendemail.thread' configuration
        value; if that is unspecified, default to --thread.
 
 
@@ -230,6 +241,12 @@ have been specified, in which case default to 'compose'.
 --dry-run::
        Do everything except actually send the emails.
 
+--[no-]format-patch::
+       When an argument may be understood either as a reference or as a file name,
+       choose to understand it as a format-patch argument ('--format-patch')
+       or as a file name ('--no-format-patch'). By default, when such a conflict
+       occurs, git send-email will fail.
+
 --quiet::
        Make git-send-email less verbose.  One line per email should be
        all that is output.
@@ -246,12 +263,6 @@ have been specified, in which case default to 'compose'.
 Default is the value of 'sendemail.validate'; if this is not set,
 default to '--validate'.
 
---[no-]format-patch::
-       When an argument may be understood either as a reference or as a file name,
-       choose to understand it as a format-patch argument ('--format-patch')
-       or as a file name ('--no-format-patch'). By default, when such a conflict
-       occurs, git send-email will fail.
-
 
 CONFIGURATION
 -------------
index 51a4e9d6d767f34205f418ec86a6281dbf4c7b2c..89ec5364ecd447f6a7836d9c7f582dbd1d864807 100644 (file)
@@ -8,9 +8,11 @@ git-show-branch - Show branches and their commits
 SYNOPSIS
 --------
 [verse]
-'git show-branch' [--all] [--remotes] [--topo-order] [--current]
+'git show-branch' [--all] [--remotes] [--topo-order | --date-order]
+               [--current] [--color | --no-color]
                [--more=<n> | --list | --independent | --merge-base]
-               [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...
+               [--no-name | --sha1-name] [--topics]
+               [<rev> | <glob>]...
 'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]
 
 DESCRIPTION
@@ -57,6 +59,11 @@ OPTIONS
         appear in topological order (i.e., descendant commits
         are shown before their parents).
 
+--date-order::
+       This option is similar to '--topo-order' in the sense that no
+       parent comes before all of its children, but otherwise commits
+       are ordered according to their commit date.
+
 --sparse::
        By default, the output omits merges that are reachable
        from only one tip being shown.  This option makes them
@@ -107,6 +114,14 @@ OPTIONS
        When no explicit <ref> parameter is given, it defaults to the
        current branch (or `HEAD` if it is detached).
 
+--color::
+       Color the status sign (one of these: `*` `!` `+` `-`) of each commit
+       corresponding to the branch it's in.
+
+--no-color::
+       Turn off colored output, even when the configuration file gives the
+       default to color output.
+
 Note that --more, --list, --independent and --merge-base options
 are mutually exclusive.
 
index 1cc24cc47e3b9845c9d1006ee58faed1bc731c49..a42d4c85bddd539d46aaaafea6bf6412cd7c3436 100644 (file)
@@ -9,8 +9,8 @@ SYNOPSIS
 --------
 [verse]
 'git stash' list [<options>]
-'git stash' (show | drop | pop ) [<stash>]
-'git stash' apply [--index] [<stash>]
+'git stash' ( show | drop ) [<stash>]
+'git stash' ( pop | apply ) [--index] [<stash>]
 'git stash' branch <branchname> [<stash>]
 'git stash' [save [--keep-index] [<message>]]
 'git stash' clear
@@ -86,16 +86,16 @@ Applying the state can fail with conflicts; in this case, it is not
 removed from the stash list. You need to resolve the conflicts by hand
 and call `git stash drop` manually afterwards.
 +
-When no `<stash>` is given, `stash@\{0}` is assumed. See also `apply`.
-
-apply [--index] [<stash>]::
-
-       Like `pop`, but do not remove the state from the stash list.
-+
 If the `--index` option is used, then tries to reinstate not only the working
 tree's changes, but also the index's ones. However, this can fail, when you
 have conflicts (which are stored in the index, where you therefore can no
 longer apply the changes as they were originally).
++
+When no `<stash>` is given, `stash@\{0}` is assumed.
+
+apply [--index] [<stash>]::
+
+       Like `pop`, but do not remove the state from the stash list.
 
 branch <branchname> [<stash>]::
 
index 3b8df4467377d73d613f76875c725cbf8544ee77..cd8e861ce4ae33b25928a16aa213933d2a38a155 100644 (file)
@@ -9,10 +9,12 @@ git-submodule - Initialize, update or inspect submodules
 SYNOPSIS
 --------
 [verse]
-'git submodule' [--quiet] add [-b branch] [--] <repository> <path>
+'git submodule' [--quiet] add [-b branch]
+             [--reference <repository>] [--] <repository> <path>
 'git submodule' [--quiet] status [--cached] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
-'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--] [<path>...]
+'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
+             [--reference <repository>] [--] [<path>...]
 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
 'git submodule' [--quiet] foreach <command>
 'git submodule' [--quiet] sync [--] [<path>...]
@@ -113,7 +115,8 @@ init::
 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.
+       This will make the submodules HEAD be detached unless '--rebase' is
+       specified or the key `submodule.$name.update` is set to `rebase`.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
@@ -177,6 +180,23 @@ OPTIONS
        This option is only valid for the update command.
        Don't fetch new objects from the remote site.
 
+--rebase::
+       This option is only valid for the update command.
+       Rebase the current branch onto the commit recorded in the
+       superproject. If this option is given, the submodule's HEAD will not
+       be detached. If a a merge failure prevents this process, you will have
+       to resolve these failures with linkgit:git-rebase[1].
+       If the key `submodule.$name.update` is set to `rebase`, this option is
+       implicit.
+
+--reference <repository>::
+       This option is only valid for add and update commands.  These
+       commands sometimes need to clone a remote repository. In this case,
+       this option will be passed to the linkgit:git-clone[1] command.
++
+*NOTE*: Do *not* use this option unless you have read the note
+for linkgit:git-clone[1]'s --reference and --shared options carefully.
+
 <path>...::
        Paths to submodule(s). When specified this will restrict the command
        to only operate on the submodules found at the specified paths.
index 1c40894669d6f86e7dbb97d86ef9ee6f2a76190d..ca3fc3de1fcfc3509a9d5d1ff268c9673bafc3c0 100644 (file)
@@ -398,6 +398,14 @@ after the authors-file is modified should continue operation.
 
 config key: svn.authorsfile
 
+--authors-prog=<filename>::
+
+If this option is specified, for each SVN committer name that does not
+exist in the authors file, the given file is executed with the committer
+name as the first argument.  The program is expected to return a single
+line of the form "Name <email>", which will be treated as if included in
+the authors file.
+
 -q::
 --quiet::
        Make 'git-svn' less verbose. Specify a second time to make it
index 3589a12e49cc6547469f0bd5c254cc547fd2863f..56d47709ac9ec698405215ecac13d66d7c773848 100644 (file)
@@ -43,7 +43,12 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.3/git.html[documentation for release 1.6.3]
+* link:v1.6.3.2/git.html[documentation for release 1.6.3.2]
+
+* release notes for
+  link:RelNotes-1.6.3.2.txt[1.6.3.2],
+  link:RelNotes-1.6.3.1.txt[1.6.3.1],
+  link:RelNotes-1.6.3.txt[1.6.3].
 
 * release notes for
   link:RelNotes-1.6.2.5.txt[1.6.2.5],
index d1a17e2625890245341a2099cc2b058e63564da2..1b67f0a9f12974afca2e8e6948d0fca564b9b17b 100644 (file)
@@ -30,6 +30,15 @@ submodule.<name>.path::
 submodule.<name>.url::
        Defines an url from where the submodule repository can be cloned.
 
+submodule.<name>.update::
+       Defines what to do when the submodule is updated by the superproject.
+       If 'checkout' (the default), the new commit specified in the
+       superproject will be checked out in the submodule on a detached HEAD.
+       If 'rebase', the current branch of the submodule will be rebased onto
+       the commit specified in the superproject.
+       This config option is overridden if 'git submodule update' is given
+       the '--rebase' option.
+
 
 EXAMPLES
 --------
index 4832bc75e2604c997e004018023e3b93cb394b87..c0f96e70708cab74a13ec8696db003289f99b455 100644 (file)
@@ -23,7 +23,7 @@ merge.tool::
        Controls which merge resolution program is used by
        linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
        "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff",
-       "diffuse", "ecmerge", "tortoisemerge", and
+       "diffuse", "ecmerge", "tortoisemerge", "araxis", and
        "opendiff".  Any other value is treated is custom merge tool
        and there must be a corresponding mergetool.<tool>.cmd option.
 
index e30c602f476da2da632d2cd04396d168c80ab233..50f9e9ac1708f3f754023c1bb60416adc9c73c74 100644 (file)
@@ -60,13 +60,13 @@ Steps to parse options
 . in `cmd_foo(int argc, const char **argv, const char *prefix)`
   call
 
-       argc = parse_options(argc, argv, builtin_foo_options, builtin_foo_usage, flags);
+       argc = parse_options(argc, argv, prefix, builtin_foo_options, builtin_foo_usage, flags);
 +
 `parse_options()` will filter out the processed options of `argv[]` and leave the
 non-option arguments in `argv[]`.
 `argc` is updated appropriately because of the assignment.
 +
-You can also pass NULL instead of a usage array as fourth parameter of
+You can also pass NULL instead of a usage array as the fifth parameter of
 parse_options(), to avoid displaying a help screen with usage info and
 option list.  This should only be done if necessary, e.g. to implement
 a limited parser for only a subset of the options that needs to be run
@@ -137,6 +137,10 @@ There are some macros to easily define options:
        Introduce a boolean option.
        If used, `int_var` is bitwise-ored with `mask`.
 
+`OPT_NEGBIT(short, long, &int_var, description, mask)`::
+       Introduce a boolean option.
+       If used, `int_var` is bitwise-anded with the inverted `mask`.
+
 `OPT_SET_INT(short, long, &int_var, description, integer)`::
        Introduce a boolean option.
        If used, set `int_var` to `integer`.
@@ -163,9 +167,22 @@ There are some macros to easily define options:
        and the result will be put into `var`.
        See 'Option Callbacks' below for a more elaborate description.
 
+`OPT_FILENAME(short, long, &var, description)`::
+       Introduce an option with a filename argument.
+       The filename will be prefixed by passing the filename along with
+       the prefix argument of `parse_options()` to `prefix_filename()`.
+
 `OPT_ARGUMENT(long, description)`::
        Introduce a long-option argument that will be kept in `argv[]`.
 
+`OPT_NUMBER_CALLBACK(&var, description, func_ptr)`::
+       Recognize numerical options like -123 and feed the integer as
+       if it was an argument to the function given by `func_ptr`.
+       The result will be put into `var`.  There can be only one such
+       option definition.  It cannot be negated and it takes no
+       arguments.  Short options that happen to be digits take
+       precedence over it.
+
 
 The last element of the array must be `OPT_END()`.
 
index 0673f0db9f044c294446fb5df1c87f83a335d6f8..39cde784c947fae6a2f294caac1898b9199d769d 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.3.2
+DEF_VER=v1.6.3.GIT
 
 LF='
 '
index d21b4eb54f936fb4a142d9b4844636ddaac00e1d..41ab8e9e0db59647b2e872757ec05e86f615ea1c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,11 @@ all::
 
 # Define V=1 to have a more verbose compile.
 #
+# Define SHELL_PATH to a POSIX shell if your /bin/sh is broken.
+#
+# Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
+# to PATH if your tools in /usr/bin are broken.
+#
 # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
 # or vsnprintf() return -1 instead of number of characters which would
 # have been written to the final string if enough space had been available.
@@ -52,6 +57,10 @@ all::
 #
 # Define NO_MKDTEMP if you don't have mkdtemp in the C library.
 #
+# Define NO_MKSTEMPS if you don't have mkstemps in the C library.
+#
+# Define NO_LIBGEN_H if you don't have libgen.h.
+#
 # Define NO_SYS_SELECT_H if you don't have sys/select.h.
 #
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
@@ -91,6 +100,10 @@ all::
 # Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
 # Patrick Mauritz).
 #
+# Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough.
+# Notably on Solaris hstrerror resides in libresolv and on Solaris 7
+# inet_ntop and inet_pton additionally reside there.
+#
 # Define NO_MMAP if you want to avoid mmap.
 #
 # Define NO_PTHREADS if you do not have or do not want to use Pthreads.
@@ -178,6 +191,9 @@ all::
 #
 # Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed
 # programs as a tar, where bin/ and libexec/ might be on different file systems.
+#
+# Define USE_NED_ALLOCATOR if you want to replace the platforms default
+# memory allocators with the nedmalloc allocator written by Niall Douglas.
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -228,6 +244,7 @@ ETC_GITCONFIG = etc/gitconfig
 endif
 lib = lib
 # DESTDIR=
+pathsep = :
 
 # default configuration for gitweb
 GITWEB_CONFIG = gitweb_config.perl
@@ -335,7 +352,6 @@ PROGRAMS += git-index-pack$X
 PROGRAMS += git-merge-index$X
 PROGRAMS += git-merge-tree$X
 PROGRAMS += git-mktag$X
-PROGRAMS += git-mktree$X
 PROGRAMS += git-pack-redundant$X
 PROGRAMS += git-patch-id$X
 PROGRAMS += git-shell$X
@@ -589,6 +605,7 @@ BUILTIN_OBJS += builtin-merge-base.o
 BUILTIN_OBJS += builtin-merge-file.o
 BUILTIN_OBJS += builtin-merge-ours.o
 BUILTIN_OBJS += builtin-merge-recursive.o
+BUILTIN_OBJS += builtin-mktree.o
 BUILTIN_OBJS += builtin-mv.o
 BUILTIN_OBJS += builtin-name-rev.o
 BUILTIN_OBJS += builtin-pack-objects.o
@@ -635,10 +652,12 @@ EXTLIBS =
 
 ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
+       NO_MKSTEMPS = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
        NO_STRLCPY = YesPlease
+       NO_MKSTEMPS = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),UnixWare)
@@ -650,6 +669,7 @@ ifeq ($(uname_S),UnixWare)
        SHELL_PATH = /usr/local/bin/bash
        NO_IPV6 = YesPlease
        NO_HSTRERROR = YesPlease
+       NO_MKSTEMPS = YesPlease
        BASIC_CFLAGS += -Kthread
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
@@ -673,6 +693,7 @@ ifeq ($(uname_S),SCO_SV)
        SHELL_PATH = /usr/bin/bash
        NO_IPV6 = YesPlease
        NO_HSTRERROR = YesPlease
+       NO_MKSTEMPS = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
        NO_STRCASESTR = YesPlease
@@ -697,11 +718,21 @@ ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
        NEEDS_NSL = YesPlease
        SHELL_PATH = /bin/bash
+       SANE_TOOL_PATH = /usr/xpg6/bin:/usr/xpg4/bin
        NO_STRCASESTR = YesPlease
        NO_MEMMEM = YesPlease
-       NO_HSTRERROR = YesPlease
        NO_MKDTEMP = YesPlease
-       OLD_ICONV = UnfortunatelyYes
+       NO_MKSTEMPS = YesPlease
+       ifeq ($(uname_R),5.7)
+               NEEDS_RESOLV = YesPlease
+               NO_IPV6 = YesPlease
+               NO_SOCKADDR_STORAGE = YesPlease
+               NO_UNSETENV = YesPlease
+               NO_SETENV = YesPlease
+               NO_STRLCPY = YesPlease
+               NO_C99_FORMAT = YesPlease
+               NO_STRTOUMAX = YesPlease
+       endif
        ifeq ($(uname_R),5.8)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
@@ -714,15 +745,19 @@ ifeq ($(uname_S),SunOS)
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
-       INSTALL = ginstall
+       ifdef NO_IPV6
+               NEEDS_RESOLV = YesPlease
+       endif
+       INSTALL = /usr/ucb/install
        TAR = gtar
-       BASIC_CFLAGS += -D__EXTENSIONS__
+       BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__
 endif
 ifeq ($(uname_O),Cygwin)
        NO_D_TYPE_IN_DIRENT = YesPlease
        NO_D_INO_IN_DIRENT = YesPlease
        NO_STRCASESTR = YesPlease
        NO_MEMMEM = YesPlease
+       NO_MKSTEMPS = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
@@ -766,11 +801,13 @@ ifeq ($(uname_S),NetBSD)
        BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
        THREADED_DELTA_SEARCH = YesPlease
        USE_ST_TIMESPEC = YesPlease
+       NO_MKSTEMPS = YesPlease
 endif
 ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
        NO_MKDTEMP = YesPlease
+       NO_MKSTEMPS = YesPlease
        NO_STRLCPY = YesPlease
        NO_NSEC = YesPlease
        FREAD_READS_DIRECTORIES = UnfortunatelyYes
@@ -786,12 +823,14 @@ endif
 ifeq ($(uname_S),GNU)
        # GNU/Hurd
        NO_STRLCPY=YesPlease
+       NO_MKSTEMPS = YesPlease
 endif
 ifeq ($(uname_S),IRIX64)
        NO_IPV6=YesPlease
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
+       NO_MKSTEMPS = YesPlease
        NO_STRLCPY = YesPlease
        NO_SOCKADDR_STORAGE=YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
@@ -804,6 +843,7 @@ ifeq ($(uname_S),HP-UX)
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
+       NO_MKSTEMPS = YesPlease
        NO_STRLCPY = YesPlease
        NO_MKDTEMP = YesPlease
        NO_UNSETENV = YesPlease
@@ -816,9 +856,10 @@ ifneq (,$(findstring CYGWIN,$(uname_S)))
        UNRELIABLE_FSTAT = UnfortunatelyYes
 endif
 ifneq (,$(findstring MINGW,$(uname_S)))
+       pathsep = ;
        NO_PREAD = YesPlease
        NO_OPENSSL = YesPlease
-       NO_CURL = YesPlease
+       NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NO_IPV6 = YesPlease
        NO_SETENV = YesPlease
@@ -826,12 +867,12 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
        NO_MEMMEM = YesPlease
-       NO_PTHREADS = YesPlease
        NEEDS_LIBICONV = YesPlease
        OLD_ICONV = YesPlease
        NO_C99_FORMAT = YesPlease
        NO_STRTOUMAX = YesPlease
        NO_MKDTEMP = YesPlease
+       NO_MKSTEMPS = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        NO_SVN_TESTS = YesPlease
        NO_PERL_MAKEMAKER = YesPlease
@@ -840,22 +881,43 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        NO_NSEC = YesPlease
        USE_WIN32_MMAP = YesPlease
+       USE_NED_ALLOCATOR = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
-       COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o
        EXTLIBS += -lws2_32
        X = .exe
+ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
+       htmldir=doc/git/html/
+       prefix =
+       INSTALL = /bin/install
+       EXTLIBS += /mingw/lib/libz.a
+       NO_R_TO_GCC_LINKER = YesPlease
+       INTERNAL_QSORT = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
+else
+       NO_CURL = YesPlease
+       NO_PTHREADS = YesPlease
+endif
 endif
 ifneq (,$(findstring arm,$(uname_M)))
        ARM_SHA1 = YesPlease
+       NO_MKSTEMPS = YesPlease
 endif
 
 -include config.mak.autogen
 -include config.mak
 
+ifdef SANE_TOOL_PATH
+SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH))
+BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix $(SANE_TOOL_PATH_SQ)|'
+PATH := $(SANE_TOOL_PATH):${PATH}
+else
+BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
+endif
+
 ifeq ($(uname_S),Darwin)
        ifndef NO_FINK
                ifeq ($(shell test -d /sw/lib && echo y),y)
@@ -882,6 +944,11 @@ ifndef CC_LD_DYNPATH
        endif
 endif
 
+ifdef NO_LIBGEN_H
+       COMPAT_CFLAGS += -DNO_LIBGEN_H
+       COMPAT_OBJS += compat/basename.o
+endif
+
 ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
 else
@@ -954,6 +1021,9 @@ endif
 ifdef NEEDS_NSL
        EXTLIBS += -lnsl
 endif
+ifdef NEEDS_RESOLV
+       EXTLIBS += -lresolv
+endif
 ifdef NO_D_TYPE_IN_DIRENT
        BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT
 endif
@@ -1009,6 +1079,10 @@ ifdef NO_MKDTEMP
        COMPAT_CFLAGS += -DNO_MKDTEMP
        COMPAT_OBJS += compat/mkdtemp.o
 endif
+ifdef NO_MKSTEMPS
+       COMPAT_CFLAGS += -DNO_MKSTEMPS
+       COMPAT_OBJS += compat/mkstemps.o
+endif
 ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
        COMPAT_OBJS += compat/unsetenv.o
@@ -1127,6 +1201,11 @@ ifdef UNRELIABLE_FSTAT
        BASIC_CFLAGS += -DUNRELIABLE_FSTAT
 endif
 
+ifdef USE_NED_ALLOCATOR
+       COMPAT_CFLAGS += -DUSE_NED_ALLOCATOR -DOVERRIDE_STRDUP -DNDEBUG -DREPLACE_SYSTEM_ALLOCATOR -Icompat/nedmalloc
+       COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1201,7 +1280,7 @@ SHELL = $(SHELL_PATH)
 
 all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
-       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
+       $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
 endif
 
 all::
@@ -1252,9 +1331,9 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
-           -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+           -e $(BROKEN_PATH_FIX) \
            $@.sh >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
@@ -1271,7 +1350,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
        sed -e '1{' \
            -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
            -e '        h' \
-           -e '        s=.*=use lib (split(/:/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
+           -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
            -e '        H' \
            -e '        x' \
            -e '}' \
@@ -1494,6 +1573,8 @@ test-delta$X: diff-delta.o patch-delta.o
 
 test-parse-options$X: parse-options.o
 
+test-parse-options.o: parse-options.h
+
 .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
 
 test-%$X: test-%.o $(GITLIBS)
@@ -1641,7 +1722,7 @@ distclean: clean
        $(RM) configure
 
 clean:
-       $(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
+       $(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
index a433be58b7bdd4a7004f9762e1f44c4aafecd1b3..f8e49a5070afc21fbd3db9320841300fe93c570f 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.3.2.txt
\ No newline at end of file
+Documentation/RelNotes-1.6.4.txt
\ No newline at end of file
index b2b90d31700d3aa50a3ac0626c1a8561c37c49b7..0bca9ca4038ace99c32eff2e042290b03b2324b7 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -309,7 +309,7 @@ static int parse_archive_args(int argc, const char **argv,
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, opts, archive_usage, 0);
+       argc = parse_options(argc, argv, NULL, opts, archive_usage, 0);
 
        if (remote)
                die("Unexpected option --remote");
diff --git a/attr.c b/attr.c
index 98eb636f13d314c20e18e646c074e2511b3c891c..f8f6faa94fd7eb4e260a75b82ef372e632f5eb9a 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -35,8 +35,7 @@ static struct git_attr *(git_attr_hash[HASHSIZE]);
 
 static unsigned hash_name(const char *name, int namelen)
 {
-       unsigned val = 0;
-       unsigned char c;
+       unsigned val = 0, c;
 
        while (namelen--) {
                c = *name++;
index b88270f90844095b3d352cc4213cbebd95a7f420..b417a15bbc83fff7180078a4cf9f73603477a295 100644 (file)
--- a/base85.c
+++ b/base85.c
@@ -91,7 +91,7 @@ void encode_85(char *buf, const unsigned char *data, int bytes)
                unsigned acc = 0;
                int cnt;
                for (cnt = 24; cnt >= 0; cnt -= 8) {
-                       int ch = *data++;
+                       unsigned ch = *data++;
                        acc |= ch << cnt;
                        if (--bytes == 0)
                                break;
index 58f7e6f7738def184072247f934cce06d349c91d..6fdff05722c3afa8e8e4cdcac200697e4116ea8e 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -6,15 +6,30 @@
 #include "list-objects.h"
 #include "quote.h"
 #include "sha1-lookup.h"
+#include "run-command.h"
+#include "log-tree.h"
 #include "bisect.h"
 
-static unsigned char (*skipped_sha1)[20];
-static int skipped_sha1_nr;
-static int skipped_sha1_alloc;
+struct sha1_array {
+       unsigned char (*sha1)[20];
+       int sha1_nr;
+       int sha1_alloc;
+       int sorted;
+};
+
+static struct sha1_array good_revs;
+static struct sha1_array skipped_revs;
+
+static const unsigned char *current_bad_sha1;
+
+struct argv_array {
+       const char **argv;
+       int argv_nr;
+       int argv_alloc;
+};
 
-static const char **rev_argv;
-static int rev_argv_nr;
-static int rev_argv_alloc;
+static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
+static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
 
 /* bits #0-15 in revision.h */
 
@@ -398,23 +413,37 @@ struct commit_list *find_bisection(struct commit_list *list,
        return best;
 }
 
+static void argv_array_push(struct argv_array *array, const char *string)
+{
+       ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc);
+       array->argv[array->argv_nr++] = string;
+}
+
+static void argv_array_push_sha1(struct argv_array *array,
+                                const unsigned char *sha1,
+                                const char *format)
+{
+       struct strbuf buf = STRBUF_INIT;
+       strbuf_addf(&buf, format, sha1_to_hex(sha1));
+       argv_array_push(array, strbuf_detach(&buf, NULL));
+}
+
+static void sha1_array_push(struct sha1_array *array,
+                           const unsigned char *sha1)
+{
+       ALLOC_GROW(array->sha1, array->sha1_nr + 1, array->sha1_alloc);
+       hashcpy(array->sha1[array->sha1_nr++], sha1);
+}
+
 static int register_ref(const char *refname, const unsigned char *sha1,
                        int flags, void *cb_data)
 {
        if (!strcmp(refname, "bad")) {
-               ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
-               rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1));
+               current_bad_sha1 = sha1;
        } else if (!prefixcmp(refname, "good-")) {
-               const char *hex = sha1_to_hex(sha1);
-               char *good = xmalloc(strlen(hex) + 2);
-               *good = '^';
-               memcpy(good + 1, hex, strlen(hex) + 1);
-               ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
-               rev_argv[rev_argv_nr++] = good;
+               sha1_array_push(&good_revs, sha1);
        } else if (!prefixcmp(refname, "skip-")) {
-               ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1,
-                          skipped_sha1_alloc);
-               hashcpy(skipped_sha1[skipped_sha1_nr++], sha1);
+               sha1_array_push(&skipped_revs, sha1);
        }
 
        return 0;
@@ -425,7 +454,7 @@ static int read_bisect_refs(void)
        return for_each_ref_in("refs/bisect/", register_ref, NULL);
 }
 
-void read_bisect_paths(void)
+void read_bisect_paths(struct argv_array *array)
 {
        struct strbuf str = STRBUF_INIT;
        const char *filename = git_path("BISECT_NAMES");
@@ -440,8 +469,8 @@ void read_bisect_paths(void)
 
                strbuf_trim(&str);
                quoted = strbuf_detach(&str, NULL);
-               res = sq_dequote_to_argv(quoted, &rev_argv,
-                                        &rev_argv_nr, &rev_argv_alloc);
+               res = sq_dequote_to_argv(quoted, &array->argv,
+                                        &array->argv_nr, &array->argv_alloc);
                if (res)
                        die("Badly quoted content in file '%s': %s",
                            filename, quoted);
@@ -451,106 +480,500 @@ void read_bisect_paths(void)
        fclose(fp);
 }
 
-static int skipcmp(const void *a, const void *b)
+static int array_cmp(const void *a, const void *b)
 {
        return hashcmp(a, b);
 }
 
-static void prepare_skipped(void)
+static void sort_sha1_array(struct sha1_array *array)
 {
-       qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp);
+       qsort(array->sha1, array->sha1_nr, sizeof(*array->sha1), array_cmp);
+
+       array->sorted = 1;
 }
 
-static const unsigned char *skipped_sha1_access(size_t index, void *table)
+static const unsigned char *sha1_access(size_t index, void *table)
 {
-       unsigned char (*skipped)[20] = table;
-       return skipped[index];
+       unsigned char (*array)[20] = table;
+       return array[index];
 }
 
-static int lookup_skipped(unsigned char *sha1)
+static int lookup_sha1_array(struct sha1_array *array,
+                            const unsigned char *sha1)
 {
-       return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr,
-                       skipped_sha1_access);
+       if (!array->sorted)
+               sort_sha1_array(array);
+
+       return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access);
 }
 
+static char *join_sha1_array_hex(struct sha1_array *array, char delim)
+{
+       struct strbuf joined_hexs = STRBUF_INIT;
+       int i;
+
+       for (i = 0; i < array->sha1_nr; i++) {
+               strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i]));
+               if (i + 1 < array->sha1_nr)
+                       strbuf_addch(&joined_hexs, delim);
+       }
+
+       return strbuf_detach(&joined_hexs, NULL);
+}
+
+/*
+ * In this function, passing a not NULL skipped_first is very special.
+ * It means that we want to know if the first commit in the list is
+ * skipped because we will want to test a commit away from it if it is
+ * indeed skipped.
+ * So if the first commit is skipped, we cannot take the shortcut to
+ * just "return list" when we find the first non skipped commit, we
+ * have to return a fully filtered list.
+ *
+ * We use (*skipped_first == -1) to mean "it has been found that the
+ * first commit is not skipped". In this case *skipped_first is set back
+ * to 0 just before the function returns.
+ */
 struct commit_list *filter_skipped(struct commit_list *list,
                                   struct commit_list **tried,
-                                  int show_all)
+                                  int show_all,
+                                  int *count,
+                                  int *skipped_first)
 {
        struct commit_list *filtered = NULL, **f = &filtered;
 
        *tried = NULL;
 
-       if (!skipped_sha1_nr)
-               return list;
+       if (skipped_first)
+               *skipped_first = 0;
+       if (count)
+               *count = 0;
 
-       prepare_skipped();
+       if (!skipped_revs.sha1_nr)
+               return list;
 
        while (list) {
                struct commit_list *next = list->next;
                list->next = NULL;
-               if (0 <= lookup_skipped(list->item->object.sha1)) {
+               if (0 <= lookup_sha1_array(&skipped_revs,
+                                          list->item->object.sha1)) {
+                       if (skipped_first && !*skipped_first)
+                               *skipped_first = 1;
                        /* Move current to tried list */
                        *tried = list;
                        tried = &list->next;
                } else {
-                       if (!show_all)
-                               return list;
+                       if (!show_all) {
+                               if (!skipped_first || !*skipped_first)
+                                       return list;
+                       } else if (skipped_first && !*skipped_first) {
+                               /* This means we know it's not skipped */
+                               *skipped_first = -1;
+                       }
                        /* Move current to filtered list */
                        *f = list;
                        f = &list->next;
+                       if (count)
+                               (*count)++;
                }
                list = next;
        }
 
+       if (skipped_first && *skipped_first == -1)
+               *skipped_first = 0;
+
        return filtered;
 }
 
-static void bisect_rev_setup(struct rev_info *revs, const char *prefix)
+static struct commit_list *apply_skip_ratio(struct commit_list *list,
+                                           int count,
+                                           int skip_num, int skip_denom)
+{
+       int index, i;
+       struct commit_list *cur, *previous;
+
+       cur = list;
+       previous = NULL;
+       index = count * skip_num / skip_denom;
+
+       for (i = 0; cur; cur = cur->next, i++) {
+               if (i == index) {
+                       if (hashcmp(cur->item->object.sha1, current_bad_sha1))
+                               return cur;
+                       if (previous)
+                               return previous;
+                       return list;
+               }
+               previous = cur;
+       }
+
+       return list;
+}
+
+static struct commit_list *managed_skipped(struct commit_list *list,
+                                          struct commit_list **tried)
+{
+       int count, skipped_first;
+       int skip_num, skip_denom;
+
+       *tried = NULL;
+
+       if (!skipped_revs.sha1_nr)
+               return list;
+
+       list = filter_skipped(list, tried, 0, &count, &skipped_first);
+
+       if (!skipped_first)
+               return list;
+
+       /* Use alternatively 1/5, 2/5 and 3/5 as skip ratio. */
+       skip_num = count % 3 + 1;
+       skip_denom = 5;
+
+       return apply_skip_ratio(list, count, skip_num, skip_denom);
+}
+
+static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
+                            const char *bad_format, const char *good_format,
+                            int read_paths)
 {
+       struct argv_array rev_argv = { NULL, 0, 0 };
+       int i;
+
        init_revisions(revs, prefix);
        revs->abbrev = 0;
        revs->commit_format = CMIT_FMT_UNSPECIFIED;
 
-       /* argv[0] will be ignored by setup_revisions */
-       ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
-       rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup");
+       /* rev_argv.argv[0] will be ignored by setup_revisions */
+       argv_array_push(&rev_argv, xstrdup("bisect_rev_setup"));
+       argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format);
+       for (i = 0; i < good_revs.sha1_nr; i++)
+               argv_array_push_sha1(&rev_argv, good_revs.sha1[i],
+                                    good_format);
+       argv_array_push(&rev_argv, xstrdup("--"));
+       if (read_paths)
+               read_bisect_paths(&rev_argv);
+       argv_array_push(&rev_argv, NULL);
+
+       setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL);
+}
 
-       if (read_bisect_refs())
-               die("reading bisect refs failed");
+static void bisect_common(struct rev_info *revs)
+{
+       if (prepare_revision_walk(revs))
+               die("revision walk setup failed");
+       if (revs->tree_objects)
+               mark_edges_uninteresting(revs->commits, revs, NULL);
+}
 
-       ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
-       rev_argv[rev_argv_nr++] = xstrdup("--");
+static void exit_if_skipped_commits(struct commit_list *tried,
+                                   const unsigned char *bad)
+{
+       if (!tried)
+               return;
+
+       printf("There are only 'skip'ped commits left to test.\n"
+              "The first bad commit could be any of:\n");
+       print_commit_list(tried, "%s\n", "%s\n");
+       if (bad)
+               printf("%s\n", sha1_to_hex(bad));
+       printf("We cannot bisect more!\n");
+       exit(2);
+}
+
+static int is_expected_rev(const unsigned char *sha1)
+{
+       const char *filename = git_path("BISECT_EXPECTED_REV");
+       struct stat st;
+       struct strbuf str = STRBUF_INIT;
+       FILE *fp;
+       int res = 0;
+
+       if (stat(filename, &st) || !S_ISREG(st.st_mode))
+               return 0;
+
+       fp = fopen(filename, "r");
+       if (!fp)
+               return 0;
 
-       read_bisect_paths();
+       if (strbuf_getline(&str, fp, '\n') != EOF)
+               res = !strcmp(str.buf, sha1_to_hex(sha1));
 
-       ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
-       rev_argv[rev_argv_nr++] = NULL;
+       strbuf_release(&str);
+       fclose(fp);
 
-       setup_revisions(rev_argv_nr, rev_argv, revs, NULL);
+       return res;
+}
+
+static void mark_expected_rev(char *bisect_rev_hex)
+{
+       int len = strlen(bisect_rev_hex);
+       const char *filename = git_path("BISECT_EXPECTED_REV");
+       int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
-       revs->limited = 1;
+       if (fd < 0)
+               die("could not create file '%s': %s",
+                   filename, strerror(errno));
+
+       bisect_rev_hex[len] = '\n';
+       write_or_die(fd, bisect_rev_hex, len + 1);
+       bisect_rev_hex[len] = '\0';
+
+       if (close(fd) < 0)
+               die("closing file %s: %s", filename, strerror(errno));
 }
 
-int bisect_next_vars(const char *prefix)
+static int bisect_checkout(char *bisect_rev_hex)
+{
+       int res;
+
+       mark_expected_rev(bisect_rev_hex);
+
+       argv_checkout[2] = bisect_rev_hex;
+       res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
+       if (res)
+               exit(res);
+
+       argv_show_branch[1] = bisect_rev_hex;
+       return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
+}
+
+static struct commit *get_commit_reference(const unsigned char *sha1)
+{
+       struct commit *r = lookup_commit_reference(sha1);
+       if (!r)
+               die("Not a valid commit name %s", sha1_to_hex(sha1));
+       return r;
+}
+
+static struct commit **get_bad_and_good_commits(int *rev_nr)
+{
+       int len = 1 + good_revs.sha1_nr;
+       struct commit **rev = xmalloc(len * sizeof(*rev));
+       int i, n = 0;
+
+       rev[n++] = get_commit_reference(current_bad_sha1);
+       for (i = 0; i < good_revs.sha1_nr; i++)
+               rev[n++] = get_commit_reference(good_revs.sha1[i]);
+       *rev_nr = n;
+
+       return rev;
+}
+
+static void handle_bad_merge_base(void)
+{
+       if (is_expected_rev(current_bad_sha1)) {
+               char *bad_hex = sha1_to_hex(current_bad_sha1);
+               char *good_hex = join_sha1_array_hex(&good_revs, ' ');
+
+               fprintf(stderr, "The merge base %s is bad.\n"
+                       "This means the bug has been fixed "
+                       "between %s and [%s].\n",
+                       bad_hex, bad_hex, good_hex);
+
+               exit(3);
+       }
+
+       fprintf(stderr, "Some good revs are not ancestor of the bad rev.\n"
+               "git bisect cannot work properly in this case.\n"
+               "Maybe you mistake good and bad revs?\n");
+       exit(1);
+}
+
+void handle_skipped_merge_base(const unsigned char *mb)
+{
+       char *mb_hex = sha1_to_hex(mb);
+       char *bad_hex = sha1_to_hex(current_bad_sha1);
+       char *good_hex = join_sha1_array_hex(&good_revs, ' ');
+
+       fprintf(stderr, "Warning: the merge base between %s and [%s] "
+               "must be skipped.\n"
+               "So we cannot be sure the first bad commit is "
+               "between %s and %s.\n"
+               "We continue anyway.\n",
+               bad_hex, good_hex, mb_hex, bad_hex);
+       free(good_hex);
+}
+
+/*
+ * "check_merge_bases" checks that merge bases are not "bad".
+ *
+ * - If one is "bad", it means the user assumed something wrong
+ * and we must exit with a non 0 error code.
+ * - If one is "good", that's good, we have nothing to do.
+ * - If one is "skipped", we can't know but we should warn.
+ * - If we don't know, we should check it out and ask the user to test.
+ */
+static void check_merge_bases(void)
+{
+       struct commit_list *result;
+       int rev_nr;
+       struct commit **rev = get_bad_and_good_commits(&rev_nr);
+
+       result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
+
+       for (; result; result = result->next) {
+               const unsigned char *mb = result->item->object.sha1;
+               if (!hashcmp(mb, current_bad_sha1)) {
+                       handle_bad_merge_base();
+               } else if (0 <= lookup_sha1_array(&good_revs, mb)) {
+                       continue;
+               } else if (0 <= lookup_sha1_array(&skipped_revs, mb)) {
+                       handle_skipped_merge_base(mb);
+               } else {
+                       printf("Bisecting: a merge base must be tested\n");
+                       exit(bisect_checkout(sha1_to_hex(mb)));
+               }
+       }
+
+       free(rev);
+       free_commit_list(result);
+}
+
+static int check_ancestors(const char *prefix)
 {
        struct rev_info revs;
-       struct rev_list_info info;
-       int reaches = 0, all = 0;
+       struct object_array pending_copy;
+       int i, res;
 
-       memset(&info, 0, sizeof(info));
-       info.revs = &revs;
-       info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED;
+       bisect_rev_setup(&revs, prefix, "^%s", "%s", 0);
 
-       bisect_rev_setup(&revs, prefix);
+       /* Save pending objects, so they can be cleaned up later. */
+       memset(&pending_copy, 0, sizeof(pending_copy));
+       for (i = 0; i < revs.pending.nr; i++)
+               add_object_array(revs.pending.objects[i].item,
+                                revs.pending.objects[i].name,
+                                &pending_copy);
 
-       if (prepare_revision_walk(&revs))
-               die("revision walk setup failed");
-       if (revs.tree_objects)
-               mark_edges_uninteresting(revs.commits, &revs, NULL);
+       bisect_common(&revs);
+       res = (revs.commits != NULL);
+
+       /* Clean up objects used, as they will be reused. */
+       for (i = 0; i < pending_copy.nr; i++) {
+               struct object *o = pending_copy.objects[i].item;
+               clear_commit_marks((struct commit *)o, ALL_REV_FLAGS);
+       }
+
+       return res;
+}
+
+/*
+ * "check_good_are_ancestors_of_bad" checks that all "good" revs are
+ * ancestor of the "bad" rev.
+ *
+ * If that's not the case, we need to check the merge bases.
+ * If a merge base must be tested by the user, its source code will be
+ * checked out to be tested by the user and we will exit.
+ */
+static void check_good_are_ancestors_of_bad(const char *prefix)
+{
+       const char *filename = git_path("BISECT_ANCESTORS_OK");
+       struct stat st;
+       int fd;
+
+       if (!current_bad_sha1)
+               die("a bad revision is needed");
+
+       /* Check if file BISECT_ANCESTORS_OK exists. */
+       if (!stat(filename, &st) && S_ISREG(st.st_mode))
+               return;
+
+       /* Bisecting with no good rev is ok. */
+       if (good_revs.sha1_nr == 0)
+               return;
+
+       /* Check if all good revs are ancestor of the bad rev. */
+       if (check_ancestors(prefix))
+               check_merge_bases();
+
+       /* Create file BISECT_ANCESTORS_OK. */
+       fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+       if (fd < 0)
+               warning("could not create file '%s': %s",
+                       filename, strerror(errno));
+       else
+               close(fd);
+}
+
+/*
+ * This does "git diff-tree --pretty COMMIT" without one fork+exec.
+ */
+static void show_diff_tree(const char *prefix, struct commit *commit)
+{
+       struct rev_info opt;
+
+       /* diff-tree init */
+       init_revisions(&opt, prefix);
+       git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+       opt.abbrev = 0;
+       opt.diff = 1;
+
+       /* This is what "--pretty" does */
+       opt.verbose_header = 1;
+       opt.use_terminator = 0;
+       opt.commit_format = CMIT_FMT_DEFAULT;
+
+       /* diff-tree init */
+       if (!opt.diffopt.output_format)
+               opt.diffopt.output_format = DIFF_FORMAT_RAW;
+
+       log_tree_commit(&opt, commit);
+}
+
+/*
+ * We use the convention that exiting with an exit code 10 means that
+ * the bisection process finished successfully.
+ * In this case the calling shell script should exit 0.
+ */
+int bisect_next_all(const char *prefix)
+{
+       struct rev_info revs;
+       struct commit_list *tried;
+       int reaches = 0, all = 0, nr;
+       const unsigned char *bisect_rev;
+       char bisect_rev_hex[41];
+
+       if (read_bisect_refs())
+               die("reading bisect refs failed");
+
+       check_good_are_ancestors_of_bad(prefix);
+
+       bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
+       revs.limited = 1;
+
+       bisect_common(&revs);
 
        revs.commits = find_bisection(revs.commits, &reaches, &all,
-                                     !!skipped_sha1_nr);
+                                      !!skipped_revs.sha1_nr);
+       revs.commits = managed_skipped(revs.commits, &tried);
+
+       if (!revs.commits) {
+               /*
+                * We should exit here only if the "bad"
+                * commit is also a "skip" commit.
+                */
+               exit_if_skipped_commits(tried, NULL);
+
+               printf("%s was both good and bad\n",
+                      sha1_to_hex(current_bad_sha1));
+               exit(1);
+       }
+
+       bisect_rev = revs.commits->item->object.sha1;
+       memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41);
 
-       return show_bisect_vars(&info, reaches, all);
+       if (!hashcmp(bisect_rev, current_bad_sha1)) {
+               exit_if_skipped_commits(tried, current_bad_sha1);
+               printf("%s is first bad commit\n", bisect_rev_hex);
+               show_diff_tree(prefix, revs.commits->item);
+               /* This means the bisection process succeeded. */
+               exit(10);
+       }
+
+       nr = all - reaches - 1;
+       printf("Bisecting: %d revisions left to test after this "
+              "(roughly %d steps)\n", nr, estimate_bisect_steps(all));
+
+       return bisect_checkout(bisect_rev_hex);
 }
+
index fdba9138773f6f6a4f36655f81965e955e79b3bc..82f8fc1910a42c5c90ad3312d9aa8f35ef2b992f 100644 (file)
--- a/bisect.h
+++ b/bisect.h
@@ -7,12 +7,17 @@ extern struct commit_list *find_bisection(struct commit_list *list,
 
 extern struct commit_list *filter_skipped(struct commit_list *list,
                                          struct commit_list **tried,
-                                         int show_all);
+                                         int show_all,
+                                         int *count,
+                                         int *skipped_first);
+
+extern void print_commit_list(struct commit_list *list,
+                             const char *format_cur,
+                             const char *format_last);
 
 /* bisect_show_flags flags in struct rev_list_info */
 #define BISECT_SHOW_ALL                (1<<0)
 #define BISECT_SHOW_TRIED      (1<<1)
-#define BISECT_SHOW_STRINGED   (1<<2)
 
 struct rev_list_info {
        struct rev_info *revs;
@@ -24,6 +29,8 @@ struct rev_list_info {
 
 extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
 
-extern int bisect_next_vars(const char *prefix);
+extern int bisect_next_all(const char *prefix);
+
+extern int estimate_bisect_steps(int all);
 
 #endif
index ad889aac5bd174bf96a87b78eeb243aea89a1626..56e522127c3e73fad59d8dc96bdffdb87a3b7a44 100644 (file)
 #include "cache-tree.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
 
 static const char * const builtin_add_usage[] = {
        "git add [options] [--] <filepattern>...",
        NULL
 };
-static int patch_interactive, add_interactive;
+static int patch_interactive, add_interactive, edit_interactive;
 static int take_worktree_changes;
 
 static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
@@ -187,6 +189,51 @@ int interactive_add(int argc, const char **argv, const char *prefix)
        return status;
 }
 
+int edit_patch(int argc, const char **argv, const char *prefix)
+{
+       char *file = xstrdup(git_path("ADD_EDIT.patch"));
+       const char *apply_argv[] = { "apply", "--recount", "--cached",
+               file, NULL };
+       struct child_process child;
+       struct rev_info rev;
+       int out;
+       struct stat st;
+
+       git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+       if (read_cache() < 0)
+               die ("Could not read the index");
+
+       init_revisions(&rev, prefix);
+       rev.diffopt.context = 7;
+
+       argc = setup_revisions(argc, argv, &rev, NULL);
+       rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+       out = open(file, O_CREAT | O_WRONLY, 0644);
+       if (out < 0)
+               die ("Could not open '%s' for writing.", file);
+       rev.diffopt.file = fdopen(out, "w");
+       rev.diffopt.close_file = 1;
+       if (run_diff_files(&rev, 0))
+               die ("Could not write patch");
+
+       launch_editor(file, NULL, NULL);
+
+       if (stat(file, &st))
+               die("Could not stat '%s'", file);
+       if (!st.st_size)
+               die("Empty patch. Aborted.");
+
+       memset(&child, 0, sizeof(child));
+       child.git_cmd = 1;
+       child.argv = apply_argv;
+       if (run_command(&child))
+               die ("Could not apply '%s'", file);
+
+       unlink(file);
+       return 0;
+}
+
 static struct lock_file lock_file;
 
 static const char ignore_error[] =
@@ -201,6 +248,7 @@ static struct option builtin_add_options[] = {
        OPT_GROUP(""),
        OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
        OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
+       OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
        OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
        OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
        OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
@@ -250,14 +298,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        int add_new_files;
        int require_pathspec;
 
-       argc = parse_options(argc, argv, builtin_add_options,
-                         builtin_add_usage, 0);
+       git_config(add_config, NULL);
+
+       argc = parse_options(argc, argv, prefix, builtin_add_options,
+                         builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
        if (patch_interactive)
                add_interactive = 1;
        if (add_interactive)
-               exit(interactive_add(argc, argv, prefix));
+               exit(interactive_add(argc - 1, argv + 1, prefix));
 
-       git_config(add_config, NULL);
+       if (edit_interactive)
+               return(edit_patch(argc, argv, prefix));
+       argc--;
+       argv++;
 
        if (addremove && take_worktree_changes)
                die("-A and -u are mutually incompatible");
index a40b9822425e25272cadf6f4170ba967eacf11bf..89a5185cea0449fd4f5110bd6efbf5aebbf68b39 100644 (file)
@@ -320,6 +320,20 @@ static int name_terminate(const char *name, int namelen, int c, int terminate)
        return 1;
 }
 
+/* remove double slashes to make --index work with such filenames */
+static char *squash_slash(char *name)
+{
+       int i = 0, j = 0;
+
+       while (name[i]) {
+               if ((name[j++] = name[i++]) == '/')
+                       while (name[i] == '/')
+                               i++;
+       }
+       name[j] = '\0';
+       return name;
+}
+
 static char *find_name(const char *line, char *def, int p_value, int terminate)
 {
        int len;
@@ -349,7 +363,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
                                free(def);
                                if (root)
                                        strbuf_insert(&name, 0, root, root_len);
-                               return strbuf_detach(&name, NULL);
+                               return squash_slash(strbuf_detach(&name, NULL));
                        }
                }
                strbuf_release(&name);
@@ -369,10 +383,10 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
                        start = line;
        }
        if (!start)
-               return def;
+               return squash_slash(def);
        len = line - start;
        if (!len)
-               return def;
+               return squash_slash(def);
 
        /*
         * Generally we prefer the shorter name, especially
@@ -383,7 +397,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
        if (def) {
                int deflen = strlen(def);
                if (deflen < len && !strncmp(start, def, deflen))
-                       return def;
+                       return squash_slash(def);
                free(def);
        }
 
@@ -392,10 +406,10 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
                strcpy(ret, root);
                memcpy(ret + root_len, start, len);
                ret[root_len + len] = '\0';
-               return ret;
+               return squash_slash(ret);
        }
 
-       return xmemdupz(start, len);
+       return squash_slash(xmemdupz(start, len));
 }
 
 static int count_slashes(const char *cp)
@@ -3263,9 +3277,11 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                OPT_BOOLEAN(0, "stat", &diffstat,
                        "instead of applying the patch, output diffstat for the input"),
                { OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary,
-                 NULL, "old option, now no-op", PARSE_OPT_HIDDEN },
+                 NULL, "old option, now no-op",
+                 PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
                { OPTION_BOOLEAN, 0, "binary", &binary,
-                 NULL, "old option, now no-op", PARSE_OPT_HIDDEN },
+                 NULL, "old option, now no-op",
+                 PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
                OPT_BOOLEAN(0, "numstat", &numstat,
                        "shows number of added and deleted lines in decimal notation"),
                OPT_BOOLEAN(0, "summary", &summary,
@@ -3278,7 +3294,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                        "apply a patch without touching the working tree"),
                OPT_BOOLEAN(0, "apply", &force_apply,
                        "also apply the patch (use with --stat/--summary/--check)"),
-               OPT_STRING(0, "build-fake-ancestor", &fake_ancestor, "file",
+               OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
                        "build a temporary index based on embedded index information"),
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
                        "paths are separated with NUL character",
@@ -3313,11 +3329,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
        if (apply_default_whitespace)
                parse_whitespace_option(apply_default_whitespace);
 
-       argc = parse_options(argc, argv, builtin_apply_options,
+       argc = parse_options(argc, argv, prefix, builtin_apply_options,
                        apply_usage, 0);
-       fake_ancestor = parse_options_fix_filename(prefix, fake_ancestor);
-       if (fake_ancestor)
-               fake_ancestor = xstrdup(fake_ancestor);
 
        if (apply_with_reject)
                apply = apply_verbosely = 1;
index ab50cebba0e6798996cc007ced9079c3f0b94a29..3c5a5a7822afebf87ef6eb1f89c7c54d76844161 100644 (file)
@@ -80,7 +80,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, local_opts, NULL, PARSE_OPT_KEEP_ALL);
+       argc = parse_options(argc, argv, prefix, local_opts, NULL,
+                            PARSE_OPT_KEEP_ALL);
 
        if (output)
                create_output_file(output);
index 8fe778766a45f8f7bfceeeef9f67a2433a892ffb..5b226399e1c30b23a7b5d226a3f45efe9dbedf45 100644 (file)
@@ -4,24 +4,25 @@
 #include "bisect.h"
 
 static const char * const git_bisect_helper_usage[] = {
-       "git bisect--helper --next-vars",
+       "git bisect--helper --next-all",
        NULL
 };
 
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
-       int next_vars = 0;
+       int next_all = 0;
        struct option options[] = {
-               OPT_BOOLEAN(0, "next-vars", &next_vars,
-                           "output next bisect step variables"),
+               OPT_BOOLEAN(0, "next-all", &next_all,
+                           "perform 'git bisect next'"),
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0);
+       argc = parse_options(argc, argv, prefix, options,
+                            git_bisect_helper_usage, 0);
 
-       if (!next_vars)
+       if (!next_all)
                usage_with_options(git_bisect_helper_usage, options);
 
-       /* next-vars */
-       return bisect_next_vars(prefix);
+       /* next-all */
+       return bisect_next_all(prefix);
 }
index 0afdb16cb0e14367994fac99b60f36c292aeaac9..0c2d29a43005e0afe7c0b0754499a37156e235ef 100644 (file)
@@ -2239,7 +2239,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
        dashdash_pos = 0;
 
-       parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
+       parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
                            PARSE_OPT_KEEP_ARGV0);
        for (;;) {
                switch (parse_options_step(&ctx, options, blame_opt_usage)) {
index 91098ca9b106239916af000cb54a4bf09629e6b6..5687d6042ced29e60aa1999dc9d73eb6c38b8e77 100644 (file)
@@ -547,7 +547,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_GROUP("Generic options"),
                OPT__VERBOSE(&verbose),
-               OPT_SET_INT( 0 , "track",  &track, "set up tracking mode (see git-pull(1))",
+               OPT_SET_INT('t', "track",  &track, "set up tracking mode (see git-pull(1))",
                        BRANCH_TRACK_EXPLICIT),
                OPT_BOOLEAN( 0 , "color",  &branch_use_color, "use colored output"),
                OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
@@ -610,7 +610,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        }
        hashcpy(merge_filter_ref, head_sha1);
 
-       argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
+                            0);
        if (!!delete + !!rename + !!force_create > 1)
                usage_with_options(builtin_branch_usage, options);
 
index 43ffe7ffae90322d757e110d8693a936209236f0..590684200854ad6a71653f30d494eb191fd4a324 100644 (file)
@@ -231,7 +231,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
        if (argc != 3 && argc != 2)
                usage_with_options(cat_file_usage, options);
 
-       argc = parse_options(argc, argv, options, cat_file_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
 
        if (opt) {
                if (argc == 1)
index 15a04b7179a09492764d43c16a3ec5ff7cdd1b61..8bd043009829fb4bb40cd2f730cbffa27c3791cf 100644 (file)
@@ -69,8 +69,8 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
        int cnt, i, doubledash;
        const char *errstr = NULL;
 
-       argc = parse_options(argc, argv, check_attr_options, check_attr_usage,
-               PARSE_OPT_KEEP_DASHDASH);
+       argc = parse_options(argc, argv, prefix, check_attr_options,
+                            check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
        if (!argc)
                usage_with_options(check_attr_usage, check_attr_options);
 
index afe35e246c5ab2b262683308def787c6abce2a83..a7a5ee10f32d52c7555f85f2dcf6c567425488dd 100644 (file)
@@ -249,7 +249,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                die("invalid cache");
        }
 
-       argc = parse_options(argc, argv, builtin_checkout_index_options,
+       argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
                        builtin_checkout_index_usage, 0);
        state.force = force;
        state.quiet = quiet;
index b8a4b0139b3b9c2f731315413a73db30edaf9231..8a9a474218c128b7878647045ba471df9902636a 100644 (file)
@@ -605,7 +605,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
        opts.track = BRANCH_TRACK_UNSPECIFIED;
 
-       argc = parse_options(argc, argv, options, checkout_usage,
+       argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
        /* --track without -b should DWIM */
index c5ad33d3e665f6d1613df2dfba235b03765565ac..1c1b6d26e9987800d2a414e49e4f371a4277b96e 100644 (file)
@@ -56,7 +56,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        else
                config_set = 1;
 
-       argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
+                            0);
 
        memset(&dir, 0, sizeof(dir));
        if (ignored_only)
index ba286e0160c494244810334e3a0b43a59756e84a..5c46496a43a8fe2f91b395563b266e79a8b94429 100644 (file)
@@ -104,11 +104,12 @@ static char *get_repo_path(const char *repo, int *is_bundle)
 static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
 {
        const char *end = repo + strlen(repo), *start;
+       char *dir;
 
        /*
-        * Strip trailing slashes and /.git
+        * Strip trailing spaces, slashes and /.git
         */
-       while (repo < end && is_dir_sep(end[-1]))
+       while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
                end--;
        if (end - repo > 5 && is_dir_sep(end[-5]) &&
            !strncmp(end - 4, ".git", 4)) {
@@ -140,10 +141,33 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
        if (is_bare) {
                struct strbuf result = STRBUF_INIT;
                strbuf_addf(&result, "%.*s.git", (int)(end - start), start);
-               return strbuf_detach(&result, 0);
+               dir = strbuf_detach(&result, 0);
+       } else
+               dir = xstrndup(start, end - start);
+       /*
+        * Replace sequences of 'control' characters and whitespace
+        * with one ascii space, remove leading and trailing spaces.
+        */
+       if (*dir) {
+               char *out = dir;
+               int prev_space = 1 /* strip leading whitespace */;
+               for (end = dir; *end; ++end) {
+                       char ch = *end;
+                       if ((unsigned char)ch < '\x20')
+                               ch = '\x20';
+                       if (isspace(ch)) {
+                               if (prev_space)
+                                       continue;
+                               prev_space = 1;
+                       } else
+                               prev_space = 0;
+                       *out++ = ch;
+               }
+               *out = '\0';
+               if (out > dir && prev_space)
+                       out[-1] = '\0';
        }
-
-       return xstrndup(start, end - start);
+       return dir;
 }
 
 static void strip_trailing_slashes(char *dir)
@@ -336,7 +360,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        junk_pid = getpid();
 
-       argc = parse_options(argc, argv, builtin_clone_options,
+       argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
 
        if (argc == 0)
index baaa75cf908d57e6d8540b6483684400c0b2494b..41e222d267ca952c3bc0c8d6b1e19c0171df0c62 100644 (file)
@@ -88,13 +88,13 @@ static struct option builtin_commit_options[] = {
        OPT__VERBOSE(&verbose),
        OPT_GROUP("Commit message options"),
 
-       OPT_STRING('F', "file", &logfile, "FILE", "read log from file"),
+       OPT_FILENAME('F', "file", &logfile, "read log from file"),
        OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
        OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
        OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
        OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
        OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
-       OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"),
+       OPT_FILENAME('t', "template", &template_file, "use specified template file"),
        OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
 
        OPT_GROUP("Commit contents options"),
@@ -697,13 +697,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
 {
        int f = 0;
 
-       argc = parse_options(argc, argv, builtin_commit_options, usage, 0);
-       logfile = parse_options_fix_filename(prefix, logfile);
-       if (logfile)
-               logfile = xstrdup(logfile);
-       template_file = parse_options_fix_filename(prefix, template_file);
-       if (template_file)
-               template_file = xstrdup(template_file);
+       argc = parse_options(argc, argv, prefix, builtin_commit_options, usage,
+                            0);
 
        if (force_author && !strchr(force_author, '>'))
                force_author = find_author_by_nickname(force_author);
index a81bc8bbf033fac83ad6deecbf4a67ba44e06a0b..60915f91caff02997222d18bc625d242d95c6bb1 100644 (file)
@@ -316,7 +316,8 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
 
        config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
 
-       argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage,
+       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 + !!given_config_file > 1) {
index b814fe5070873f5c87fc6bbfde480e3b0a83e397..1b0b6c84ea4279df56b82c5a406adbea59e58513 100644 (file)
@@ -83,7 +83,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
-       argc = parse_options(argc, argv, opts, count_objects_usage, 0);
+       argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0);
        /* we do not take arguments other than flags for now */
        if (argc)
                usage_with_options(count_objects_usage, opts);
index 63c6a19da5b38bc7c00c624c080ba0afbb10ff8a..7a662980d102a275fceb8299bba0b84b41a8f9cb 100644 (file)
@@ -322,7 +322,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
-       argc = parse_options(argc, argv, options, describe_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, describe_usage, 0);
        if (max_candidates < 0)
                max_candidates = 0;
        else if (max_candidates > MAX_TAGS)
index 6731713223d4df24614417cc4285cbee793151d3..6cef8103127a8748acb64ca7ec7a7a95fb639cd3 100644 (file)
@@ -515,7 +515,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 
        init_revisions(&revs, prefix);
        argc = setup_revisions(argc, argv, &revs, NULL);
-       argc = parse_options(argc, argv, options, fast_export_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
        if (argc > 1)
                usage_with_options (fast_export_usage, options);
 
index 6202462216f3b8c3cbe924d4cc002616e69d75ab..629735f54723a3bc3b24f8c91e9a6226ae28d742 100644 (file)
@@ -483,7 +483,9 @@ static int sideband_demux(int fd, void *data)
 {
        int *xd = data;
 
-       return recv_sideband("fetch-pack", xd[0], fd);
+       int ret = recv_sideband("fetch-pack", xd[0], fd);
+       close(fd);
+       return ret;
 }
 
 static int get_pack(int xd[2], char **pack_lockfile)
index 1f7a3f1ce66434a84ff96ef352245862fe088a70..cd5eb9aff530fcb9629bd561bc774e2b9ad69afc 100644 (file)
@@ -202,7 +202,7 @@ static int update_local_ref(struct ref *ref,
        struct commit *current = NULL, *updated;
        enum object_type type;
        struct branch *current_branch = branch_get(NULL);
-       const char *pretty_ref = prettify_ref(ref);
+       const char *pretty_ref = prettify_refname(ref->name);
 
        *display = 0;
        type = sha1_object_info(ref->new_sha1, NULL);
@@ -294,7 +294,7 @@ static int update_local_ref(struct ref *ref,
        }
 }
 
-static int store_updated_refs(const char *url, const char *remote_name,
+static int store_updated_refs(const char *raw_url, const char *remote_name,
                struct ref *ref_map)
 {
        FILE *fp;
@@ -303,11 +303,13 @@ static int store_updated_refs(const char *url, const char *remote_name,
        char note[1024];
        const char *what, *kind;
        struct ref *rm;
-       char *filename = git_path("FETCH_HEAD");
+       char *url, *filename = git_path("FETCH_HEAD");
 
        fp = fopen(filename, "a");
        if (!fp)
                return error("cannot open %s: %s\n", filename, strerror(errno));
+
+       url = transport_anonymize_url(raw_url);
        for (rm = ref_map; rm; rm = rm->next) {
                struct ref *ref = NULL;
 
@@ -358,12 +360,18 @@ static int store_updated_refs(const char *url, const char *remote_name,
                                                    kind);
                        note_len += sprintf(note + note_len, "'%s' of ", what);
                }
-               note_len += sprintf(note + note_len, "%.*s", url_len, url);
-               fprintf(fp, "%s\t%s\t%s\n",
+               note[note_len] = '\0';
+               fprintf(fp, "%s\t%s\t%s",
                        sha1_to_hex(commit ? commit->object.sha1 :
                                    rm->old_sha1),
                        rm->merge ? "" : "not-for-merge",
                        note);
+               for (i = 0; i < url_len; ++i)
+                       if ('\n' == url[i])
+                               fputs("\\n", fp);
+                       else
+                               fputc(url[i], fp);
+               fputc('\n', fp);
 
                if (ref)
                        rc |= update_local_ref(ref, what, note);
@@ -381,6 +389,7 @@ static int store_updated_refs(const char *url, const char *remote_name,
                                fprintf(stderr, " %s\n", note);
                }
        }
+       free(url);
        fclose(fp);
        if (rc & STORE_REF_ERROR_DF_CONFLICT)
                error("some local refs could not be updated; try running\n"
@@ -630,7 +639,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc; i++)
                strbuf_addf(&default_rla, " %s", argv[i]);
 
-       argc = parse_options(argc, argv,
+       argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
 
        if (argc == 0)
index fae1482ba91937232f427a3f5dd03c587c3fba57..fbf9582e667dfd1e69027b35c3f307f3c7a08a99 100644 (file)
@@ -351,7 +351,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_BOOLEAN(0, "log",     &merge_summary, "populate log with the shortlog"),
                OPT_BOOLEAN(0, "summary", &merge_summary, "alias for --log"),
-               OPT_STRING('F', "file",   &inpath, "file", "file to read from"),
+               OPT_FILENAME('F', "file", &inpath, "file to read from"),
                OPT_END()
        };
 
@@ -360,10 +360,10 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        int ret;
 
        git_config(fmt_merge_msg_config, NULL);
-       argc = parse_options(argc, argv, options, fmt_merge_msg_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage,
+                            0);
        if (argc > 0)
                usage_with_options(fmt_merge_msg_usage, options);
-       inpath = parse_options_fix_filename(prefix, inpath);
 
        if (inpath && strcmp(inpath, "-")) {
                in = fopen(inpath, "r");
index d091e04af9549b70a1e15a4b845383056e71932e..d7cc8cafbfb4ea39e347828013b28d21cfe2be74 100644 (file)
@@ -561,14 +561,6 @@ static void populate_value(struct refinfo *ref)
 
        ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
 
-       buf = get_obj(ref->objectname, &obj, &size, &eaten);
-       if (!buf)
-               die("missing object %s for %s",
-                   sha1_to_hex(ref->objectname), ref->refname);
-       if (!obj)
-               die("parse_object_buffer failed on %s for %s",
-                   sha1_to_hex(ref->objectname), ref->refname);
-
        /* Fill in specials first */
        for (i = 0; i < used_atom_cnt; i++) {
                const char *name = used_atom[i];
@@ -621,6 +613,22 @@ static void populate_value(struct refinfo *ref)
                }
        }
 
+       for (i = 0; i < used_atom_cnt; i++) {
+               struct atom_value *v = &ref->value[i];
+               if (v->s == NULL)
+                       goto need_obj;
+       }
+       return;
+
+ need_obj:
+       buf = get_obj(ref->objectname, &obj, &size, &eaten);
+       if (!buf)
+               die("missing object %s for %s",
+                   sha1_to_hex(ref->objectname), ref->refname);
+       if (!obj)
+               die("parse_object_buffer failed on %s for %s",
+                   sha1_to_hex(ref->objectname), ref->refname);
+
        grab_values(ref->value, 0, obj, buf, size);
        if (!eaten)
                free(buf);
@@ -905,7 +913,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
-       parse_options(argc, argv, opts, for_each_ref_usage, 0);
+       parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
        if (maxcount < 0) {
                error("invalid --count argument: `%d'", maxcount);
                usage_with_options(for_each_ref_usage, opts);
@@ -926,7 +934,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 
        memset(&cbdata, 0, sizeof(cbdata));
        cbdata.grab_pattern = argv;
-       for_each_ref(grab_single_ref, &cbdata);
+       for_each_rawref(grab_single_ref, &cbdata);
        refs = cbdata.grab_array;
        num_refs = cbdata.grab_cnt;
 
index 6436bc224840f11af2f7fa26c61b62c25d78d865..7da706cac3bef05f03c09955d41b4e5077558d4f 100644 (file)
@@ -590,7 +590,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 
        errors_found = 0;
 
-       argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0);
+       argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
        if (write_lost_and_found) {
                check_full = 1;
                include_reflogs = 0;
index fc556ed7f3fb68734fd783e5b38e6b050bed9c5b..7d3e9cc7a04e0aa3eb378cb54b7b71fe17981c95 100644 (file)
@@ -194,7 +194,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (pack_refs < 0)
                pack_refs = !is_bare_repository();
 
-       argc = parse_options(argc, argv, builtin_gc_options, builtin_gc_usage, 0);
+       argc = parse_options(argc, argv, prefix, builtin_gc_options,
+                            builtin_gc_usage, 0);
        if (argc > 0)
                usage_with_options(builtin_gc_usage, builtin_gc_options);
 
index f88a912ace9195c387566770432a904e2d7adcb7..73fc922c4995369c0ca86c01200e2a0ea39161ab 100644 (file)
@@ -10,6 +10,7 @@
 #include "tag.h"
 #include "tree-walk.h"
 #include "builtin.h"
+#include "parse-options.h"
 #include "grep.h"
 
 #ifndef NO_EXTERNAL_GREP
 #endif
 #endif
 
-static int builtin_grep;
+static char const * const grep_usage[] = {
+       "git grep [options] [-e] <pattern> [<rev>...] [[--] path...]",
+       NULL
+};
 
 static int grep_config(const char *var, const char *value, void *cb)
 {
@@ -432,7 +436,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 }
 #endif
 
-static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
+static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
+                     int external_grep_allowed)
 {
        int hit = 0;
        int nr;
@@ -444,7 +449,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
         * we grep through the checked-out files. It tends to
         * be a lot more optimized
         */
-       if (!cached && !builtin_grep) {
+       if (!cached && external_grep_allowed) {
                hit = external_grep(opt, paths, cached);
                if (hit >= 0)
                        return hit;
@@ -560,25 +565,182 @@ static int grep_object(struct grep_opt *opt, const char **paths,
        die("unable to grep from object of type %s", typename(obj->type));
 }
 
-static const char builtin_grep_usage[] =
-"git grep <option>* [-e] <pattern> <rev>* [[--] <path>...]";
+static int context_callback(const struct option *opt, const char *arg,
+                           int unset)
+{
+       struct grep_opt *grep_opt = opt->value;
+       int value;
+       const char *endp;
+
+       if (unset) {
+               grep_opt->pre_context = grep_opt->post_context = 0;
+               return 0;
+       }
+       value = strtol(arg, (char **)&endp, 10);
+       if (*endp) {
+               return error("switch `%c' expects a numerical value",
+                            opt->short_name);
+       }
+       grep_opt->pre_context = grep_opt->post_context = value;
+       return 0;
+}
+
+static int file_callback(const struct option *opt, const char *arg, int unset)
+{
+       struct grep_opt *grep_opt = opt->value;
+       FILE *patterns;
+       int lno = 0;
+       struct strbuf sb;
+
+       patterns = fopen(arg, "r");
+       if (!patterns)
+               die("'%s': %s", arg, strerror(errno));
+       while (strbuf_getline(&sb, patterns, '\n') == 0) {
+               /* ignore empty line like grep does */
+               if (sb.len == 0)
+                       continue;
+               append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
+                                   ++lno, GREP_PATTERN);
+       }
+       fclose(patterns);
+       strbuf_release(&sb);
+       return 0;
+}
+
+static int not_callback(const struct option *opt, const char *arg, int unset)
+{
+       struct grep_opt *grep_opt = opt->value;
+       append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT);
+       return 0;
+}
+
+static int and_callback(const struct option *opt, const char *arg, int unset)
+{
+       struct grep_opt *grep_opt = opt->value;
+       append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND);
+       return 0;
+}
+
+static int open_callback(const struct option *opt, const char *arg, int unset)
+{
+       struct grep_opt *grep_opt = opt->value;
+       append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN);
+       return 0;
+}
+
+static int close_callback(const struct option *opt, const char *arg, int unset)
+{
+       struct grep_opt *grep_opt = opt->value;
+       append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN);
+       return 0;
+}
 
-static const char emsg_invalid_context_len[] =
-"%s: invalid context length argument";
-static const char emsg_missing_context_len[] =
-"missing context length argument";
-static const char emsg_missing_argument[] =
-"option requires an argument -%s";
+static int pattern_callback(const struct option *opt, const char *arg,
+                           int unset)
+{
+       struct grep_opt *grep_opt = opt->value;
+       append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN);
+       return 0;
+}
+
+static int help_callback(const struct option *opt, const char *arg, int unset)
+{
+       return -1;
+}
 
 int cmd_grep(int argc, const char **argv, const char *prefix)
 {
        int hit = 0;
        int cached = 0;
+       int external_grep_allowed = 1;
        int seen_dashdash = 0;
        struct grep_opt opt;
        struct object_array list = { 0, 0, NULL };
        const char **paths = NULL;
        int i;
+       int dummy;
+       struct option options[] = {
+               OPT_BOOLEAN(0, "cached", &cached,
+                       "search in index instead of in the work tree"),
+               OPT_GROUP(""),
+               OPT_BOOLEAN('v', "invert-match", &opt.invert,
+                       "show non-matching lines"),
+               OPT_BIT('i', "ignore-case", &opt.regflags,
+                       "case insensitive matching", REG_ICASE),
+               OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp,
+                       "match patterns only at word boundaries"),
+               OPT_SET_INT('a', "text", &opt.binary,
+                       "process binary files as text", GREP_BINARY_TEXT),
+               OPT_SET_INT('I', NULL, &opt.binary,
+                       "don't match patterns in binary files",
+                       GREP_BINARY_NOMATCH),
+               OPT_GROUP(""),
+               OPT_BIT('E', "extended-regexp", &opt.regflags,
+                       "use extended POSIX regular expressions", REG_EXTENDED),
+               OPT_NEGBIT('G', "basic-regexp", &opt.regflags,
+                       "use basic POSIX regular expressions (default)",
+                       REG_EXTENDED),
+               OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
+                       "interpret patterns as fixed strings"),
+               OPT_GROUP(""),
+               OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"),
+               OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
+               OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1),
+               OPT_NEGBIT(0, "full-name", &opt.relative,
+                       "show filenames relative to top directory", 1),
+               OPT_BOOLEAN('l', "files-with-matches", &opt.name_only,
+                       "show only filenames instead of matching lines"),
+               OPT_BOOLEAN(0, "name-only", &opt.name_only,
+                       "synonym for --files-with-matches"),
+               OPT_BOOLEAN('L', "files-without-match",
+                       &opt.unmatch_name_only,
+                       "show only the names of files without match"),
+               OPT_BOOLEAN('z', "null", &opt.null_following_name,
+                       "print NUL after filenames"),
+               OPT_BOOLEAN('c', "count", &opt.count,
+                       "show the number of matches instead of matching lines"),
+               OPT_SET_INT(0, "color", &opt.color, "highlight matches", 1),
+               OPT_GROUP(""),
+               OPT_CALLBACK('C', NULL, &opt, "n",
+                       "show <n> context lines before and after matches",
+                       context_callback),
+               OPT_INTEGER('B', NULL, &opt.pre_context,
+                       "show <n> context lines before matches"),
+               OPT_INTEGER('A', NULL, &opt.post_context,
+                       "show <n> context lines after matches"),
+               OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
+                       context_callback),
+               OPT_GROUP(""),
+               OPT_CALLBACK('f', NULL, &opt, "file",
+                       "read patterns from file", file_callback),
+               { OPTION_CALLBACK, 'e', NULL, &opt, "pattern",
+                       "match <pattern>", PARSE_OPT_NONEG, pattern_callback },
+               { OPTION_CALLBACK, 0, "and", &opt, NULL,
+                 "combine patterns specified with -e",
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
+               OPT_BOOLEAN(0, "or", &dummy, ""),
+               { OPTION_CALLBACK, 0, "not", &opt, NULL, "",
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },
+               { OPTION_CALLBACK, '(', NULL, &opt, NULL, "",
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
+                 open_callback },
+               { OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
+                 close_callback },
+               OPT_BOOLEAN(0, "all-match", &opt.all_match,
+                       "show only matches from files that match all patterns"),
+               OPT_GROUP(""),
+#if NO_EXTERNAL_GREP
+               OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
+                       "allow calling of grep(1) (ignored by this build)"),
+#else
+               OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
+                       "allow calling of grep(1) (default)"),
+#endif
+               { OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
+                 PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
+               OPT_END()
+       };
 
        memset(&opt, 0, sizeof(opt));
        opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
@@ -603,227 +765,21 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
         * unrecognized non option is the beginning of the refs list
         * that continues up to the -- (if exists), and then paths.
         */
-
-       while (1 < argc) {
-               const char *arg = argv[1];
-               argc--; argv++;
-               if (!strcmp("--cached", arg)) {
-                       cached = 1;
-                       continue;
-               }
-               if (!strcmp("--no-ext-grep", arg)) {
-                       builtin_grep = 1;
-                       continue;
-               }
-               if (!strcmp("-a", arg) ||
-                   !strcmp("--text", arg)) {
-                       opt.binary = GREP_BINARY_TEXT;
-                       continue;
-               }
-               if (!strcmp("-i", arg) ||
-                   !strcmp("--ignore-case", arg)) {
-                       opt.regflags |= REG_ICASE;
-                       continue;
-               }
-               if (!strcmp("-I", arg)) {
-                       opt.binary = GREP_BINARY_NOMATCH;
-                       continue;
-               }
-               if (!strcmp("-v", arg) ||
-                   !strcmp("--invert-match", arg)) {
-                       opt.invert = 1;
-                       continue;
-               }
-               if (!strcmp("-E", arg) ||
-                   !strcmp("--extended-regexp", arg)) {
-                       opt.regflags |= REG_EXTENDED;
-                       continue;
-               }
-               if (!strcmp("-F", arg) ||
-                   !strcmp("--fixed-strings", arg)) {
-                       opt.fixed = 1;
-                       continue;
-               }
-               if (!strcmp("-G", arg) ||
-                   !strcmp("--basic-regexp", arg)) {
-                       opt.regflags &= ~REG_EXTENDED;
-                       continue;
-               }
-               if (!strcmp("-n", arg)) {
-                       opt.linenum = 1;
-                       continue;
-               }
-               if (!strcmp("-h", arg)) {
-                       opt.pathname = 0;
-                       continue;
-               }
-               if (!strcmp("-H", arg)) {
-                       opt.pathname = 1;
-                       continue;
-               }
-               if (!strcmp("-l", arg) ||
-                   !strcmp("--name-only", arg) ||
-                   !strcmp("--files-with-matches", arg)) {
-                       opt.name_only = 1;
-                       continue;
-               }
-               if (!strcmp("-L", arg) ||
-                   !strcmp("--files-without-match", arg)) {
-                       opt.unmatch_name_only = 1;
-                       continue;
-               }
-               if (!strcmp("-z", arg) ||
-                   !strcmp("--null", arg)) {
-                       opt.null_following_name = 1;
-                       continue;
-               }
-               if (!strcmp("-c", arg) ||
-                   !strcmp("--count", arg)) {
-                       opt.count = 1;
-                       continue;
-               }
-               if (!strcmp("-w", arg) ||
-                   !strcmp("--word-regexp", arg)) {
-                       opt.word_regexp = 1;
-                       continue;
-               }
-               if (!prefixcmp(arg, "-A") ||
-                   !prefixcmp(arg, "-B") ||
-                   !prefixcmp(arg, "-C") ||
-                   (arg[0] == '-' && '1' <= arg[1] && arg[1] <= '9')) {
-                       unsigned num;
-                       const char *scan;
-                       switch (arg[1]) {
-                       case 'A': case 'B': case 'C':
-                               if (!arg[2]) {
-                                       if (argc <= 1)
-                                               die(emsg_missing_context_len);
-                                       scan = *++argv;
-                                       argc--;
-                               }
-                               else
-                                       scan = arg + 2;
-                               break;
-                       default:
-                               scan = arg + 1;
-                               break;
-                       }
-                       if (strtoul_ui(scan, 10, &num))
-                               die(emsg_invalid_context_len, scan);
-                       switch (arg[1]) {
-                       case 'A':
-                               opt.post_context = num;
-                               break;
-                       default:
-                       case 'C':
-                               opt.post_context = num;
-                       case 'B':
-                               opt.pre_context = num;
-                               break;
-                       }
-                       continue;
-               }
-               if (!strcmp("-f", arg)) {
-                       FILE *patterns;
-                       int lno = 0;
-                       char buf[1024];
-                       if (argc <= 1)
-                               die(emsg_missing_argument, arg);
-                       patterns = fopen(argv[1], "r");
-                       if (!patterns)
-                               die("'%s': %s", argv[1], strerror(errno));
-                       while (fgets(buf, sizeof(buf), patterns)) {
-                               int len = strlen(buf);
-                               if (len && buf[len-1] == '\n')
-                                       buf[len-1] = 0;
-                               /* ignore empty line like grep does */
-                               if (!buf[0])
-                                       continue;
-                               append_grep_pattern(&opt, xstrdup(buf),
-                                                   argv[1], ++lno,
-                                                   GREP_PATTERN);
-                       }
-                       fclose(patterns);
-                       argv++;
-                       argc--;
-                       continue;
-               }
-               if (!strcmp("--not", arg)) {
-                       append_grep_pattern(&opt, arg, "command line", 0,
-                                           GREP_NOT);
-                       continue;
-               }
-               if (!strcmp("--and", arg)) {
-                       append_grep_pattern(&opt, arg, "command line", 0,
-                                           GREP_AND);
-                       continue;
-               }
-               if (!strcmp("--or", arg))
-                       continue; /* no-op */
-               if (!strcmp("(", arg)) {
-                       append_grep_pattern(&opt, arg, "command line", 0,
-                                           GREP_OPEN_PAREN);
-                       continue;
-               }
-               if (!strcmp(")", arg)) {
-                       append_grep_pattern(&opt, arg, "command line", 0,
-                                           GREP_CLOSE_PAREN);
-                       continue;
-               }
-               if (!strcmp("--all-match", arg)) {
-                       opt.all_match = 1;
-                       continue;
-               }
-               if (!strcmp("-e", arg)) {
-                       if (1 < argc) {
-                               append_grep_pattern(&opt, argv[1],
-                                                   "-e option", 0,
-                                                   GREP_PATTERN);
-                               argv++;
-                               argc--;
-                               continue;
-                       }
-                       die(emsg_missing_argument, arg);
-               }
-               if (!strcmp("--full-name", arg)) {
-                       opt.relative = 0;
-                       continue;
-               }
-               if (!strcmp("--color", arg)) {
-                       opt.color = 1;
-                       continue;
-               }
-               if (!strcmp("--no-color", arg)) {
-                       opt.color = 0;
-                       continue;
-               }
-               if (!strcmp("--", arg)) {
-                       /* later processing wants to have this at argv[1] */
-                       argv--;
-                       argc++;
-                       break;
-               }
-               if (*arg == '-')
-                       usage(builtin_grep_usage);
-
-               /* First unrecognized non-option token */
-               if (!opt.pattern_list) {
-                       append_grep_pattern(&opt, arg, "command line", 0,
-                                           GREP_PATTERN);
-                       break;
-               }
-               else {
-                       /* We are looking at the first path or rev;
-                        * it is found at argv[1] after leaving the
-                        * loop.
-                        */
-                       argc++; argv--;
-                       break;
-               }
+       argc = parse_options(argc, argv, prefix, options, grep_usage,
+                            PARSE_OPT_KEEP_DASHDASH |
+                            PARSE_OPT_STOP_AT_NON_OPTION |
+                            PARSE_OPT_NO_INTERNAL_HELP);
+
+       /* First unrecognized non-option token */
+       if (argc > 0 && !opt.pattern_list) {
+               append_grep_pattern(&opt, argv[0], "command line", 0,
+                                   GREP_PATTERN);
+               argv++;
+               argc--;
        }
 
        if (opt.color && !opt.color_external)
-               builtin_grep = 1;
+               external_grep_allowed = 0;
        if (!opt.pattern_list)
                die("no pattern given.");
        if ((opt.regflags != REG_NEWLINE) && opt.fixed)
@@ -831,7 +787,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        compile_grep_patterns(&opt);
 
        /* Check revs and then paths */
-       for (i = 1; i < argc; i++) {
+       for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                unsigned char sha1[20];
                /* Is it a rev? */
@@ -874,7 +830,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        if (!list.nr) {
                if (!cached)
                        setup_work_tree();
-               return !grep_cache(&opt, paths, cached);
+               return !grep_cache(&opt, paths, cached, external_grep_allowed);
        }
 
        if (cached)
index 67dda3e6e63c9130a6f1e1c94cb35e519d765a95..6e53b23833062249485124b827da348a8be206b0 100644 (file)
@@ -80,10 +80,9 @@ static int check_emacsclient_version(void)
        ec_process.argv = argv_ec;
        ec_process.err = -1;
        ec_process.stdout_to_stderr = 1;
-       if (start_command(&ec_process)) {
-               fprintf(stderr, "Failed to start emacsclient.\n");
-               return -1;
-       }
+       if (start_command(&ec_process))
+               return error("Failed to start emacsclient.");
+
        strbuf_read(&buffer, ec_process.err, 20);
        close(ec_process.err);
 
@@ -94,20 +93,17 @@ static int check_emacsclient_version(void)
        finish_command(&ec_process);
 
        if (prefixcmp(buffer.buf, "emacsclient")) {
-               fprintf(stderr, "Failed to parse emacsclient version.\n");
                strbuf_release(&buffer);
-               return -1;
+               return error("Failed to parse emacsclient version.");
        }
 
        strbuf_remove(&buffer, 0, strlen("emacsclient"));
        version = atoi(buffer.buf);
 
        if (version < 22) {
-               fprintf(stderr,
-                       "emacsclient version '%d' too old (< 22).\n",
-                       version);
                strbuf_release(&buffer);
-               return -1;
+               return error("emacsclient version '%d' too old (< 22).",
+                       version);
        }
 
        strbuf_release(&buffer);
@@ -423,7 +419,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
        setup_git_directory_gently(&nongit);
        git_config(git_help_config, NULL);
 
-       argc = parse_options(argc, argv, builtin_help_options,
+       argc = parse_options(argc, argv, prefix, builtin_help_options,
                        builtin_help_usage, 0);
 
        if (show_all) {
index f10cfebdbbabac67dea41639289e7ad968d5bbe3..0d34050556855d6bab8b4c4121e833cc326324df 100644 (file)
@@ -18,6 +18,7 @@
 #include "shortlog.h"
 #include "remote.h"
 #include "string-list.h"
+#include "parse-options.h"
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -619,7 +620,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        struct shortlog log;
        struct strbuf sb = STRBUF_INIT;
        int i;
-       const char *encoding = "utf-8";
+       const char *encoding = "UTF-8";
        struct diff_options opts;
        int need_8bit_cte = 0;
        struct commit *commit = NULL;
@@ -740,27 +741,179 @@ static const char *set_outdir(const char *prefix, const char *output_directory)
                                       output_directory));
 }
 
+static const char * const builtin_format_patch_usage[] = {
+       "git format-patch [options] [<since> | <revision range>]",
+       NULL
+};
+
+static int keep_subject = 0;
+
+static int keep_callback(const struct option *opt, const char *arg, int unset)
+{
+       ((struct rev_info *)opt->value)->total = -1;
+       keep_subject = 1;
+       return 0;
+}
+
+static int subject_prefix = 0;
+
+static int subject_prefix_callback(const struct option *opt, const char *arg,
+                           int unset)
+{
+       subject_prefix = 1;
+       ((struct rev_info *)opt->value)->subject_prefix = arg;
+       return 0;
+}
+
+static int numbered_cmdline_opt = 0;
+
+static int numbered_callback(const struct option *opt, const char *arg,
+                            int unset)
+{
+       *(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1;
+       if (unset)
+               auto_number =  0;
+       return 0;
+}
+
+static int no_numbered_callback(const struct option *opt, const char *arg,
+                               int unset)
+{
+       return numbered_callback(opt, arg, 1);
+}
+
+static int output_directory_callback(const struct option *opt, const char *arg,
+                             int unset)
+{
+       const char **dir = (const char **)opt->value;
+       if (*dir)
+               die("Two output directories?");
+       *dir = arg;
+       return 0;
+}
+
+static int thread_callback(const struct option *opt, const char *arg, int unset)
+{
+       int *thread = (int *)opt->value;
+       if (unset)
+               *thread = 0;
+       else if (!arg || !strcmp(arg, "shallow"))
+               *thread = THREAD_SHALLOW;
+       else if (!strcmp(arg, "deep"))
+               *thread = THREAD_DEEP;
+       else
+               return 1;
+       return 0;
+}
+
+static int attach_callback(const struct option *opt, const char *arg, int unset)
+{
+       struct rev_info *rev = (struct rev_info *)opt->value;
+       if (unset)
+               rev->mime_boundary = NULL;
+       else if (arg)
+               rev->mime_boundary = arg;
+       else
+               rev->mime_boundary = git_version_string;
+       rev->no_inline = unset ? 0 : 1;
+       return 0;
+}
+
+static int inline_callback(const struct option *opt, const char *arg, int unset)
+{
+       struct rev_info *rev = (struct rev_info *)opt->value;
+       if (unset)
+               rev->mime_boundary = NULL;
+       else if (arg)
+               rev->mime_boundary = arg;
+       else
+               rev->mime_boundary = git_version_string;
+       rev->no_inline = 0;
+       return 0;
+}
+
+static int header_callback(const struct option *opt, const char *arg, int unset)
+{
+       add_header(arg);
+       return 0;
+}
+
+static int cc_callback(const struct option *opt, const char *arg, int unset)
+{
+       ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+       extra_cc[extra_cc_nr++] = xstrdup(arg);
+       return 0;
+}
+
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
        struct commit *commit;
        struct commit **list = NULL;
        struct rev_info rev;
-       int nr = 0, total, i, j;
+       int nr = 0, total, i;
        int use_stdout = 0;
        int start_number = -1;
-       int keep_subject = 0;
        int numbered_files = 0;         /* _just_ numbers */
-       int subject_prefix = 0;
        int ignore_if_in_upstream = 0;
        int cover_letter = 0;
        int boundary_count = 0;
        int no_binary_diff = 0;
-       int numbered_cmdline_opt = 0;
        struct commit *origin = NULL, *head = NULL;
        const char *in_reply_to = NULL;
        struct patch_ids ids;
        char *add_signoff = NULL;
        struct strbuf buf = STRBUF_INIT;
+       const struct option builtin_format_patch_options[] = {
+               { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
+                           "use [PATCH n/m] even with a single patch",
+                           PARSE_OPT_NOARG, numbered_callback },
+               { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
+                           "use [PATCH] even with multiple patches",
+                           PARSE_OPT_NOARG, no_numbered_callback },
+               OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"),
+               OPT_BOOLEAN(0, "stdout", &use_stdout,
+                           "print patches to standard out"),
+               OPT_BOOLEAN(0, "cover-letter", &cover_letter,
+                           "generate a cover letter"),
+               OPT_BOOLEAN(0, "numbered-files", &numbered_files,
+                           "use simple number sequence for output file names"),
+               OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx",
+                           "use <sfx> instead of '.patch'"),
+               OPT_INTEGER(0, "start-number", &start_number,
+                           "start numbering patches at <n> instead of 1"),
+               { OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix",
+                           "Use [<prefix>] instead of [PATCH]",
+                           PARSE_OPT_NONEG, subject_prefix_callback },
+               { OPTION_CALLBACK, 'o', "output-directory", &output_directory,
+                           "dir", "store resulting files in <dir>",
+                           PARSE_OPT_NONEG, output_directory_callback },
+               { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
+                           "don't strip/add [PATCH]",
+                           PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
+               OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
+                           "don't output binary diffs"),
+               OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
+                           "don't include a patch matching a commit upstream"),
+               OPT_GROUP("Messaging"),
+               { OPTION_CALLBACK, 0, "add-header", NULL, "header",
+                           "add email header", PARSE_OPT_NONEG,
+                           header_callback },
+               { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header",
+                           PARSE_OPT_NONEG, cc_callback },
+               OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id",
+                           "make first mail a reply to <message-id>"),
+               { OPTION_CALLBACK, 0, "attach", &rev, "boundary",
+                           "attach the patch", PARSE_OPT_OPTARG,
+                           attach_callback },
+               { OPTION_CALLBACK, 0, "inline", &rev, "boundary",
+                           "inline the patch",
+                           PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+                           inline_callback },
+               { OPTION_CALLBACK, 0, "thread", &thread, "style",
+                           "enable message threading, styles: shallow, deep",
+                           PARSE_OPT_OPTARG, thread_callback },
+               OPT_END()
+       };
 
        git_config(git_format_config, NULL);
        init_revisions(&rev, prefix);
@@ -783,102 +936,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
         * like "git format-patch -o a123 HEAD^.." may fail; a123 is
         * possibly a valid SHA1.
         */
-       for (i = 1, j = 1; i < argc; i++) {
-               if (!strcmp(argv[i], "--stdout"))
-                       use_stdout = 1;
-               else if (!strcmp(argv[i], "-n") ||
-                               !strcmp(argv[i], "--numbered")) {
-                       numbered = 1;
-                       numbered_cmdline_opt = 1;
-               }
-               else if (!strcmp(argv[i], "-N") ||
-                               !strcmp(argv[i], "--no-numbered")) {
-                       numbered = 0;
-                       auto_number = 0;
-               }
-               else if (!prefixcmp(argv[i], "--start-number="))
-                       start_number = strtol(argv[i] + 15, NULL, 10);
-               else if (!strcmp(argv[i], "--numbered-files"))
-                       numbered_files = 1;
-               else if (!strcmp(argv[i], "--start-number")) {
-                       i++;
-                       if (i == argc)
-                               die("Need a number for --start-number");
-                       start_number = strtol(argv[i], NULL, 10);
-               }
-               else if (!prefixcmp(argv[i], "--cc=")) {
-                       ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
-                       extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5);
-               }
-               else if (!strcmp(argv[i], "-k") ||
-                               !strcmp(argv[i], "--keep-subject")) {
-                       keep_subject = 1;
-                       rev.total = -1;
-               }
-               else if (!strcmp(argv[i], "--output-directory") ||
-                        !strcmp(argv[i], "-o")) {
-                       i++;
-                       if (argc <= i)
-                               die("Which directory?");
-                       if (output_directory)
-                               die("Two output directories?");
-                       output_directory = argv[i];
-               }
-               else if (!strcmp(argv[i], "--signoff") ||
-                        !strcmp(argv[i], "-s")) {
-                       do_signoff = 1;
-               }
-               else if (!strcmp(argv[i], "--attach")) {
-                       rev.mime_boundary = git_version_string;
-                       rev.no_inline = 1;
-               }
-               else if (!prefixcmp(argv[i], "--attach=")) {
-                       rev.mime_boundary = argv[i] + 9;
-                       rev.no_inline = 1;
-               }
-               else if (!strcmp(argv[i], "--no-attach")) {
-                       rev.mime_boundary = NULL;
-                       rev.no_inline = 0;
-               }
-               else if (!strcmp(argv[i], "--inline")) {
-                       rev.mime_boundary = git_version_string;
-                       rev.no_inline = 0;
-               }
-               else if (!prefixcmp(argv[i], "--inline=")) {
-                       rev.mime_boundary = argv[i] + 9;
-                       rev.no_inline = 0;
-               }
-               else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
-                       ignore_if_in_upstream = 1;
-               else if (!strcmp(argv[i], "--thread")
-                       || !strcmp(argv[i], "--thread=shallow"))
-                       thread = THREAD_SHALLOW;
-               else if (!strcmp(argv[i], "--thread=deep"))
-                       thread = THREAD_DEEP;
-               else if (!strcmp(argv[i], "--no-thread"))
-                       thread = 0;
-               else if (!prefixcmp(argv[i], "--in-reply-to="))
-                       in_reply_to = argv[i] + 14;
-               else if (!strcmp(argv[i], "--in-reply-to")) {
-                       i++;
-                       if (i == argc)
-                               die("Need a Message-Id for --in-reply-to");
-                       in_reply_to = argv[i];
-               } else if (!prefixcmp(argv[i], "--subject-prefix=")) {
-                       subject_prefix = 1;
-                       rev.subject_prefix = argv[i] + 17;
-               } else if (!prefixcmp(argv[i], "--suffix="))
-                       fmt_patch_suffix = argv[i] + 9;
-               else if (!strcmp(argv[i], "--cover-letter"))
-                       cover_letter = 1;
-               else if (!strcmp(argv[i], "--no-binary"))
-                       no_binary_diff = 1;
-               else if (!prefixcmp(argv[i], "--add-header="))
-                       add_header(argv[i] + 13);
-               else
-                       argv[j++] = argv[i];
-       }
-       argc = j;
+       argc = parse_options(argc, argv, prefix, builtin_format_patch_options,
+                            builtin_format_patch_usage,
+                            PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
 
        if (do_signoff) {
                const char *committer;
index da2daf45acd2154b7f00da34d41a5ab8acaf0c38..2312866605c4140ac4e5a9f5bb963ca5ab5c6da3 100644 (file)
@@ -454,7 +454,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                OPT_BIT(0, "directory", &dir.flags,
                        "show 'other' directories' name only",
                        DIR_SHOW_OTHER_DIRECTORIES),
-               OPT_BIT(0, "no-empty-directory", &dir.flags,
+               OPT_NEGBIT(0, "empty-directory", &dir.flags,
                        "don't show empty directories",
                        DIR_HIDE_EMPTY_DIRECTORIES),
                OPT_BOOLEAN('u', "unmerged", &show_unmerged,
@@ -486,7 +486,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                prefix_offset = strlen(prefix);
        git_config(git_default_config, NULL);
 
-       argc = parse_options(argc, argv, builtin_ls_files_options,
+       argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
                        ls_files_usage, 0);
        if (show_tag || show_valid_bit) {
                tag_cached = "H ";
index 1eeeb4de6d0d54e3fd753b7f057351094e10a24e..92637ac0bae82d0b88e267b572a51a75299cda5c 100644 (file)
@@ -193,8 +193,7 @@ static void handle_content_type(struct strbuf *line)
                *content_top = boundary;
                boundary = NULL;
        }
-       if (slurp_attr(line->buf, "charset=", &charset))
-               strbuf_tolower(&charset);
+       slurp_attr(line->buf, "charset=", &charset);
 
        if (boundary) {
                strbuf_release(boundary);
@@ -481,7 +480,7 @@ static const char *guess_charset(const struct strbuf *line, const char *target_c
                if (is_utf8(line->buf))
                        return NULL;
        }
-       return "latin1";
+       return "ISO8859-1";
 }
 
 static void convert_to_utf8(struct strbuf *line, const char *charset)
@@ -494,7 +493,7 @@ static void convert_to_utf8(struct strbuf *line, const char *charset)
                        return;
        }
 
-       if (!strcmp(metainfo_charset, charset))
+       if (!strcasecmp(metainfo_charset, charset))
                return;
        out = reencode_string(line->buf, metainfo_charset, charset);
        if (!out)
@@ -550,7 +549,6 @@ static int decode_header_bq(struct strbuf *it)
                if (cp + 3 - it->buf > it->len)
                        goto decode_header_bq_out;
                strbuf_add(&charset_q, ep, cp - ep);
-               strbuf_tolower(&charset_q);
 
                encoding = cp[1];
                if (!encoding || cp[2] != '?')
@@ -944,7 +942,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
         */
        git_config(git_default_config, NULL);
 
-       def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8");
+       def_charset = (git_commit_encoding ? git_commit_encoding : "UTF-8");
        metainfo_charset = def_charset;
 
        while (1 < argc && argv[1][0] == '-') {
index 03fc1c211453f1ed09ee2c6b71d438b0bfbf474f..a6ec2f7ab76db27afbb63aacf3bba60b4e73b3f6 100644 (file)
@@ -53,7 +53,7 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        };
 
        git_config(git_default_config, NULL);
-       argc = parse_options(argc, argv, options, merge_base_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
        if (argc < 2)
                usage_with_options(merge_base_usage, options);
        rev = xmalloc(argc * sizeof(*rev));
index 96edb97a8327ba64cccf64bfa341e94d9f903e94..afd2ea7a732460c431328dbd4c0141a49d638244 100644 (file)
@@ -48,7 +48,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                        merge_style = git_xmerge_style;
        }
 
-       argc = parse_options(argc, argv, options, merge_file_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
        if (argc != 3)
                usage_with_options(merge_file_usage, options);
        if (quiet) {
index 703045bfc84a804f5cf58537117fb17859951f7e..d26a96e486f2a84fdfc54527d15b9acdf7ea34bb 100644 (file)
@@ -45,8 +45,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                        bases[bases_count++] = sha;
                }
                else
-                       warning("Cannot handle more than %zu bases. "
-                               "Ignoring %s.", ARRAY_SIZE(bases)-1, argv[i]);
+                       warning("Cannot handle more than %d bases. "
+                               "Ignoring %s.",
+                               (int)ARRAY_SIZE(bases)-1, argv[i]);
        }
        if (argc - i != 3) /* "--" "<head>" "<remote>" */
                die("Not handling anything other than two heads merge.");
index 0b58e5eda1f38c1560cc9dcf69be37fa5fc9886f..793f2f4a189e448fb481c5c1e2886be28fddf2e2 100644 (file)
@@ -462,7 +462,7 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
-               parse_options(argc, argv, builtin_merge_options,
+               parse_options(argc, argv, NULL, builtin_merge_options,
                              builtin_merge_usage, 0);
                free(buf);
        }
@@ -836,8 +836,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        struct commit_list **remotes = &remoteheads;
 
        setup_work_tree();
+       if (file_exists(git_path("MERGE_HEAD")))
+               die("You have not concluded your merge. (MERGE_HEAD exists)");
        if (read_cache_unmerged())
-               die("You are in the middle of a conflicted merge.");
+               die("You are in the middle of a conflicted merge."
+                               " (index unmerged)");
 
        /*
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
@@ -855,7 +858,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
 
-       argc = parse_options(argc, argv, builtin_merge_options,
+       argc = parse_options(argc, argv, prefix, builtin_merge_options,
                        builtin_merge_usage, 0);
        if (verbosity < 0)
                show_diffstat = 0;
diff --git a/builtin-mktree.c b/builtin-mktree.c
new file mode 100644 (file)
index 0000000..098395f
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * GIT - the stupid content tracker
+ *
+ * Copyright (c) Junio C Hamano, 2006, 2009
+ */
+#include "builtin.h"
+#include "quote.h"
+#include "tree.h"
+#include "parse-options.h"
+
+static struct treeent {
+       unsigned mode;
+       unsigned char sha1[20];
+       int len;
+       char name[FLEX_ARRAY];
+} **entries;
+static int alloc, used;
+
+static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
+{
+       struct treeent *ent;
+       int len = strlen(path);
+       if (strchr(path, '/'))
+               die("path %s contains slash", path);
+
+       if (alloc <= used) {
+               alloc = alloc_nr(used);
+               entries = xrealloc(entries, sizeof(*entries) * alloc);
+       }
+       ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1);
+       ent->mode = mode;
+       ent->len = len;
+       hashcpy(ent->sha1, sha1);
+       memcpy(ent->name, path, len+1);
+}
+
+static int ent_compare(const void *a_, const void *b_)
+{
+       struct treeent *a = *(struct treeent **)a_;
+       struct treeent *b = *(struct treeent **)b_;
+       return base_name_compare(a->name, a->len, a->mode,
+                                b->name, b->len, b->mode);
+}
+
+static void write_tree(unsigned char *sha1)
+{
+       struct strbuf buf;
+       size_t size;
+       int i;
+
+       qsort(entries, used, sizeof(*entries), ent_compare);
+       for (size = i = 0; i < used; i++)
+               size += 32 + entries[i]->len;
+
+       strbuf_init(&buf, size);
+       for (i = 0; i < used; i++) {
+               struct treeent *ent = entries[i];
+               strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0');
+               strbuf_add(&buf, ent->sha1, 20);
+       }
+
+       write_sha1_file(buf.buf, buf.len, tree_type, sha1);
+}
+
+static const char *mktree_usage[] = {
+       "git mktree [-z] [--missing] [--batch]",
+       NULL
+};
+
+static void mktree_line(char *buf, size_t len, int line_termination, int allow_missing)
+{
+       char *ptr, *ntr;
+       unsigned mode;
+       enum object_type mode_type; /* object type derived from mode */
+       enum object_type obj_type; /* object type derived from sha */
+       char *path;
+       unsigned char sha1[20];
+
+       ptr = buf;
+       /*
+        * Read non-recursive ls-tree output format:
+        *     mode SP type SP sha1 TAB name
+        */
+       mode = strtoul(ptr, &ntr, 8);
+       if (ptr == ntr || !ntr || *ntr != ' ')
+               die("input format error: %s", buf);
+       ptr = ntr + 1; /* type */
+       ntr = strchr(ptr, ' ');
+       if (!ntr || buf + len <= ntr + 40 ||
+           ntr[41] != '\t' ||
+           get_sha1_hex(ntr + 1, sha1))
+               die("input format error: %s", buf);
+
+       /* It is perfectly normal if we do not have a commit from a submodule */
+       if (S_ISGITLINK(mode))
+               allow_missing = 1;
+
+
+       *ntr++ = 0; /* now at the beginning of SHA1 */
+
+       path = ntr + 41;  /* at the beginning of name */
+       if (line_termination && path[0] == '"') {
+               struct strbuf p_uq = STRBUF_INIT;
+               if (unquote_c_style(&p_uq, path, NULL))
+                       die("invalid quoting");
+               path = strbuf_detach(&p_uq, NULL);
+       }
+
+       /*
+        * Object type is redundantly derivable three ways.
+        * These should all agree.
+        */
+       mode_type = object_type(mode);
+       if (mode_type != type_from_string(ptr)) {
+               die("entry '%s' object type (%s) doesn't match mode type (%s)",
+                       path, ptr, typename(mode_type));
+       }
+
+       /* Check the type of object identified by sha1 */
+       obj_type = sha1_object_info(sha1, NULL);
+       if (obj_type < 0) {
+               if (allow_missing) {
+                       ; /* no problem - missing objects are presumed to be of the right type */
+               } else {
+                       die("entry '%s' object %s is unavailable", path, sha1_to_hex(sha1));
+               }
+       } else {
+               if (obj_type != mode_type) {
+                       /*
+                        * The object exists but is of the wrong type.
+                        * This is a problem regardless of allow_missing
+                        * because the new tree entry will never be correct.
+                        */
+                       die("entry '%s' object %s is a %s but specified type was (%s)",
+                               path, sha1_to_hex(sha1), typename(obj_type), typename(mode_type));
+               }
+       }
+
+       append_to_tree(mode, sha1, path);
+}
+
+int cmd_mktree(int ac, const char **av, const char *prefix)
+{
+       struct strbuf sb = STRBUF_INIT;
+       unsigned char sha1[20];
+       int line_termination = '\n';
+       int allow_missing = 0;
+       int is_batch_mode = 0;
+       int got_eof = 0;
+
+       const struct option option[] = {
+               OPT_SET_INT('z', NULL, &line_termination, "input is NUL terminated", '\0'),
+               OPT_SET_INT( 0 , "missing", &allow_missing, "allow missing objects", 1),
+               OPT_SET_INT( 0 , "batch", &is_batch_mode, "allow creation of more than one tree", 1),
+               OPT_END()
+       };
+
+       ac = parse_options(ac, av, prefix, option, mktree_usage, 0);
+
+       while (!got_eof) {
+               while (1) {
+                       if (strbuf_getline(&sb, stdin, line_termination) == EOF) {
+                               got_eof = 1;
+                               break;
+                       }
+                       if (sb.buf[0] == '\0') {
+                               /* empty lines denote tree boundaries in batch mode */
+                               if (is_batch_mode)
+                                       break;
+                               die("input format error: (blank line only valid in batch mode)");
+                       }
+                       mktree_line(sb.buf, sb.len, line_termination, allow_missing);
+               }
+               if (is_batch_mode && got_eof && used < 1) {
+                       /*
+                        * Execution gets here if the last tree entry is terminated with a
+                        * new-line.  The final new-line has been made optional to be
+                        * consistent with the original non-batch behaviour of mktree.
+                        */
+                       ; /* skip creating an empty tree */
+               } else {
+                       write_tree(sha1);
+                       puts(sha1_to_hex(sha1));
+                       fflush(stdout);
+               }
+               used=0; /* reset tree entry buffer for re-use in batch mode */
+       }
+       strbuf_release(&sb);
+       exit(0);
+}
index 01270fefdfb04ed27379b1ca761a811b929ce887..8b81d4b51d78bb2aa49430102ab585afce9cccdf 100644 (file)
@@ -72,7 +72,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        if (read_cache() < 0)
                die("index file corrupt");
 
-       argc = parse_options(argc, argv, builtin_mv_options, builtin_mv_usage, 0);
+       argc = parse_options(argc, argv, prefix, builtin_mv_options,
+                            builtin_mv_usage, 0);
        if (--argc < 1)
                usage_with_options(builtin_mv_usage, builtin_mv_options);
 
index 08c8aabf9428447abad7def693d7b22c5330e180..06a38ac8c10126085e6477c205d450da089faae2 100644 (file)
@@ -238,7 +238,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
        };
 
        git_config(git_default_config, NULL);
-       argc = parse_options(argc, argv, opts, name_rev_usage, 0);
+       argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
        if (!!all + !!transform_stdin + !!argc > 1) {
                error("Specify either a list, or --all, not both!");
                usage_with_options(name_rev_usage, opts);
index 9742b45c4da7f9330491d0b4c6d3ed60aadb0f4c..941cc2d73cf5ee6791a0cee8c409dcdb9756b448 100644 (file)
@@ -653,8 +653,7 @@ static void rehash_objects(void)
 
 static unsigned name_hash(const char *name)
 {
-       unsigned char c;
-       unsigned hash = 0;
+       unsigned c, hash = 0;
 
        if (!name)
                return 0;
index 34246df4ec946273d9f42e6f0848b02d8510beea..091860b2e370561f0811399b0d307e732e9eaac3 100644 (file)
@@ -15,7 +15,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
                OPT_BIT(0, "prune", &flags, "prune loose refs (default)", PACK_REFS_PRUNE),
                OPT_END(),
        };
-       if (parse_options(argc, argv, opts, pack_refs_usage, 0))
+       if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
                usage_with_options(pack_refs_usage, opts);
        return pack_refs(flags);
 }
index 145ba83651e9c8560e20d6ab449ce64783bc65f5..0ed9cce4a23aa575c1e131a808cedd4f8bb542f4 100644 (file)
@@ -142,7 +142,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
        init_revisions(&revs, prefix);
 
-       argc = parse_options(argc, argv, options, prune_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
        while (argc--) {
                unsigned char sha1[20];
                const char *name = *argv++;
index 2eabcd3bdfb3f5d5705125a8f74d21d4ab1deafc..c869974013b5c1bb563d4f9b306820362545c9ea 100644 (file)
@@ -198,7 +198,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, options, push_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, push_usage, 0);
 
        if (tags)
                add_refspec("refs/tags/*");
index 0b08da9b595432efa231eb59d88ecd2b422eb56a..33d345dc3993d01d3cd5ae1cc0aa5ae8eda83edb 100644 (file)
@@ -192,7 +192,6 @@ static int run_receive_hook(const char *hook_name)
 static int run_update_hook(struct command *cmd)
 {
        static const char update_hook[] = "hooks/update";
-       struct child_process proc;
        const char *argv[5];
 
        if (access(update_hook, X_OK) < 0)
@@ -204,12 +203,9 @@ static int run_update_hook(struct command *cmd)
        argv[3] = sha1_to_hex(cmd->new_sha1);
        argv[4] = NULL;
 
-       memset(&proc, 0, sizeof(proc));
-       proc.argv = argv;
-       proc.no_stdin = 1;
-       proc.stdout_to_stderr = 1;
-
-       return hook_status(run_command(&proc), update_hook);
+       return hook_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
+                                       RUN_COMMAND_STDOUT_TO_STDERR),
+                       update_hook);
 }
 
 static int is_ref_checked_out(const char *ref)
index fda9a54a0c2f31b1071e4c401a3707375fc7a10d..dfc0b9e706507fc557cc2b593632fda4683387bc 100644 (file)
@@ -79,7 +79,8 @@ static int add(int argc, const char **argv)
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+                            0);
 
        if (argc < 2)
                usage_with_options(builtin_remote_usage, options);
@@ -294,17 +295,14 @@ static int get_push_ref_states(const struct ref *remote_refs,
        struct ref_states *states)
 {
        struct remote *remote = states->remote;
-       struct ref *ref, *local_refs, *push_map, **push_tail;
+       struct ref *ref, *local_refs, *push_map;
        if (remote->mirror)
                return 0;
 
        local_refs = get_local_heads();
        push_map = copy_ref_list(remote_refs);
 
-       push_tail = &push_map;
-       while (*push_tail)
-               push_tail = &((*push_tail)->next);
-       match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr,
+       match_refs(local_refs, &push_map, remote->push_refspec_nr,
                   remote->push_refspec, MATCH_REFS_NONE);
 
        states->push.strdup_strings = 1;
@@ -986,7 +984,8 @@ static int show(int argc, const char **argv)
        struct string_list info_list = { NULL, 0, 0, 0 };
        struct show_info info;
 
-       argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+                            0);
 
        if (argc < 1)
                return show_all();
@@ -1003,9 +1002,12 @@ static int show(int argc, const char **argv)
 
                get_remote_ref_states(*argv, &states, query_flag);
 
-               printf("* remote %s\n  URL: %s\n", *argv,
-                       states.remote->url_nr > 0 ?
-                               states.remote->url[0] : "(no URL)");
+               printf("* remote %s\n", *argv);
+               if (states.remote->url_nr) {
+                       for (i=0; i < states.remote->url_nr; i++)
+                               printf("  URL: %s\n", states.remote->url[i]);
+               } else
+                       printf("  URL: %s\n", "(no URL)");
                if (no_query)
                        printf("  HEAD branch: (not queried)\n");
                else if (!states.heads.nr)
@@ -1076,7 +1078,8 @@ static int set_head(int argc, const char **argv)
                            "delete refs/remotes/<name>/HEAD"),
                OPT_END()
        };
-       argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+                            0);
        if (argc)
                strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
 
@@ -1130,7 +1133,8 @@ static int prune(int argc, const char **argv)
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+                            0);
 
        if (argc < 1)
                usage_with_options(builtin_remote_usage, options);
@@ -1220,7 +1224,7 @@ static int update(int argc, const char **argv)
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, options, builtin_remote_usage,
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
                             PARSE_OPT_KEEP_ARGV0);
        if (argc < 2) {
                argc = 2;
@@ -1306,7 +1310,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
        };
        int result;
 
-       argc = parse_options(argc, argv, options, builtin_remote_usage,
+       argc = parse_options(argc, argv, prefix, options, builtin_remote_usage,
                PARSE_OPT_STOP_AT_NON_OPTION);
 
        if (argc < 1)
index 7e7ebabaa865ebd749502774638efe7b6d26bec1..5fa1789d0c2b90ef9ee83d091b67292f2ff0db26 100644 (file)
@@ -203,7 +203,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       argc = parse_options(argc, argv, options, git_reset_usage,
+       argc = parse_options(argc, argv, prefix, options, git_reset_usage,
                                                PARSE_OPT_KEEP_DASHDASH);
        reflog_action = args_to_str(argv);
        setenv("GIT_REFLOG_ACTION", reflog_action, 0);
index 38a8f234de8120d15eaff9ee0d342e334ee006ca..4ba1c12e0b6fedb056aebed6ffc8f8bb8d6cfd18 100644 (file)
@@ -211,7 +211,7 @@ static inline int exp2i(int n)
  *
  * and P(2^n + x) < 0.5 means 2^n < 3x
  */
-static int estimate_bisect_steps(int all)
+int estimate_bisect_steps(int all)
 {
        int n, x, e;
 
@@ -225,27 +225,46 @@ static int estimate_bisect_steps(int all)
        return (e < 3 * x) ? n : n - 1;
 }
 
-static void show_tried_revs(struct commit_list *tried, int stringed)
+void print_commit_list(struct commit_list *list,
+                      const char *format_cur,
+                      const char *format_last)
 {
-       printf("bisect_tried='");
-       for (;tried; tried = tried->next) {
-               char *format = tried->next ? "%s|" : "%s";
-               printf(format, sha1_to_hex(tried->item->object.sha1));
+       for ( ; list; list = list->next) {
+               const char *format = list->next ? format_cur : format_last;
+               printf(format, sha1_to_hex(list->item->object.sha1));
        }
-       printf(stringed ? "' &&\n" : "'\n");
+}
+
+static void show_tried_revs(struct commit_list *tried)
+{
+       printf("bisect_tried='");
+       print_commit_list(tried, "%s|", "%s");
+       printf("'\n");
+}
+
+static void print_var_str(const char *var, const char *val)
+{
+       printf("%s='%s'\n", var, val);
+}
+
+static void print_var_int(const char *var, int val)
+{
+       printf("%s=%d\n", var, val);
 }
 
 int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
 {
        int cnt, flags = info->bisect_show_flags;
-       char hex[41] = "", *format;
+       char hex[41] = "";
        struct commit_list *tried;
        struct rev_info *revs = info->revs;
 
        if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
                return 1;
 
-       revs->commits = filter_skipped(revs->commits, &tried, flags & BISECT_SHOW_ALL);
+       revs->commits = filter_skipped(revs->commits, &tried,
+                                      flags & BISECT_SHOW_ALL,
+                                      NULL, NULL);
 
        /*
         * revs->commits can reach "reaches" commits among
@@ -269,28 +288,14 @@ int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
        }
 
        if (flags & BISECT_SHOW_TRIED)
-               show_tried_revs(tried, flags & BISECT_SHOW_STRINGED);
-       format = (flags & BISECT_SHOW_STRINGED) ?
-               "bisect_rev=%s &&\n"
-               "bisect_nr=%d &&\n"
-               "bisect_good=%d &&\n"
-               "bisect_bad=%d &&\n"
-               "bisect_all=%d &&\n"
-               "bisect_steps=%d\n"
-               :
-               "bisect_rev=%s\n"
-               "bisect_nr=%d\n"
-               "bisect_good=%d\n"
-               "bisect_bad=%d\n"
-               "bisect_all=%d\n"
-               "bisect_steps=%d\n";
-       printf(format,
-              hex,
-              cnt - 1,
-              all - reaches - 1,
-              reaches - 1,
-              all,
-              estimate_bisect_steps(all));
+               show_tried_revs(tried);
+
+       print_var_str("bisect_rev", hex);
+       print_var_int("bisect_nr", cnt - 1);
+       print_var_int("bisect_good", all - reaches - 1);
+       print_var_int("bisect_bad", reaches - 1);
+       print_var_int("bisect_all", all);
+       print_var_int("bisect_steps", estimate_bisect_steps(all));
 
        return 0;
 }
index 22c6d6ad161f0de7dee88336a81a8f1f873c0bb0..112d622cda6f74f9752627d9adf3a1cdb04851c3 100644 (file)
@@ -318,7 +318,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        int onb = 0, osz = 0, unb = 0, usz = 0;
 
        strbuf_addstr(&parsed, "set --");
-       argc = parse_options(argc, argv, parseopt_opts, parseopt_usage,
+       argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage,
                             PARSE_OPT_KEEP_DASHDASH);
        if (argc < 1 || strcmp(argv[0], "--"))
                usage_with_options(parseopt_usage, parseopt_opts);
@@ -393,7 +393,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        /* put an OPT_END() */
        ALLOC_GROW(opts, onb + 1, osz);
        memset(opts + onb, 0, sizeof(opts[onb]));
-       argc = parse_options(argc, argv, opts, usage,
+       argc = parse_options(argc, argv, prefix, opts, usage,
                             keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0);
 
        strbuf_addf(&parsed, " --");
@@ -402,6 +402,18 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static int cmd_sq_quote(int argc, const char **argv)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       if (argc)
+               sq_quote_argv(&buf, argv, 0);
+       printf("%s\n", buf.buf);
+       strbuf_release(&buf);
+
+       return 0;
+}
+
 static void die_no_single_rev(int quiet)
 {
        if (quiet)
@@ -419,6 +431,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        if (argc > 1 && !strcmp("--parseopt", argv[1]))
                return cmd_parseopt(argc - 1, argv + 1, prefix);
 
+       if (argc > 1 && !strcmp("--sq-quote", argv[1]))
+               return cmd_sq_quote(argc - 2, argv + 2);
+
        prefix = setup_git_directory();
        git_config(git_default_config, NULL);
        for (i = 1; i < argc; i++) {
index 3f2614e1bbef009afadefff7d284225533db2a09..c87115af30abd9515599586af860ccd07b96b264 100644 (file)
@@ -60,7 +60,7 @@ static void parse_args(int argc, const char **argv)
                OPT_END(),
        };
 
-       if (parse_options(argc, argv, options, usage_str, 0) != 1)
+       if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1)
                usage_with_options(usage_str, options);
        arg = argv[0];
 
@@ -323,9 +323,9 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 
        encoding = get_encoding(message);
        if (!encoding)
-               encoding = "utf-8";
+               encoding = "UTF-8";
        if (!git_commit_encoding)
-               git_commit_encoding = "utf-8";
+               git_commit_encoding = "UTF-8";
        if ((reencoded_message = reencode_string(message,
                                        git_commit_encoding, encoding)))
                message = reencoded_message;
index 269d60890ac6732f05b835e88bdb60a10bb3d441..0cc491271846214d73d55a4f528c8854e0460c8f 100644 (file)
@@ -157,7 +157,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       argc = parse_options(argc, argv, builtin_rm_options, builtin_rm_usage, 0);
+       argc = parse_options(argc, argv, prefix, builtin_rm_options,
+                            builtin_rm_usage, 0);
        if (!argc)
                usage_with_options(builtin_rm_usage, builtin_rm_options);
 
index 473a3de40c4cc9aab5c1355d0602646234e7bc1c..c375a3dbde0a75af592c8881965f255f66b599d1 100644 (file)
@@ -178,9 +178,9 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
 {
        fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
        if (from)
-               fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+               fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
        else
-               fputs(prettify_ref(to), stderr);
+               fputs(prettify_refname(to->name), stderr);
        if (msg) {
                fputs(" (", stderr);
                fputs(msg, stderr);
@@ -473,7 +473,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        int fd[2];
        struct child_process *conn;
        struct extra_have_objects extra_have;
-       struct ref *remote_refs, **remote_tail, *local_refs;
+       struct ref *remote_refs, *local_refs;
        int ret;
        int send_all = 0;
        const char *receivepack = "git-receive-pack";
@@ -567,13 +567,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                flags |= MATCH_REFS_MIRROR;
 
        /* match them up */
-       remote_tail = &remote_refs;
-       while (*remote_tail)
-               remote_tail = &((*remote_tail)->next);
-       if (match_refs(local_refs, remote_refs, &remote_tail,
-                      nr_refspecs, refspecs, flags)) {
+       if (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
                return -1;
-       }
 
        ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
 
index b28091b4455db15e80841f2779ce0686d4f18826..6a3812ee18a2ca1bc251f67582ebaee3675d9af5 100644 (file)
@@ -263,7 +263,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
        git_config(git_default_config, NULL);
        shortlog_init(&log);
        init_revisions(&rev, prefix);
-       parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
+       parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
                            PARSE_OPT_KEEP_ARGV0);
 
        for (;;) {
index c3afabbe914d699a8e0c80bdb5063ca51506167e..01bea3b583b53f4930a1ab17d64b1b714f58099c 100644 (file)
@@ -2,11 +2,26 @@
 #include "commit.h"
 #include "refs.h"
 #include "builtin.h"
+#include "color.h"
+#include "parse-options.h"
 
-static const char show_branch_usage[] =
-"git show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...] | --reflog[=n[,b]] <branch>";
-static const char show_branch_usage_reflog[] =
-"--reflog is incompatible with --all, --remotes, --independent or --merge-base";
+static const char* show_branch_usage[] = {
+    "git show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base] [--topics] [--color] [<refs>...]",
+    "--reflog[=n[,b]] [--list] [--color] <branch>",
+    NULL
+};
+
+static int showbranch_use_color = -1;
+static char column_colors[][COLOR_MAXLEN] = {
+       GIT_COLOR_RED,
+       GIT_COLOR_GREEN,
+       GIT_COLOR_YELLOW,
+       GIT_COLOR_BLUE,
+       GIT_COLOR_MAGENTA,
+       GIT_COLOR_CYAN,
+};
+
+#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
 
 static int default_num;
 static int default_alloc;
@@ -19,6 +34,20 @@ static const char **default_arg;
 
 #define DEFAULT_REFLOG 4
 
+static const char *get_color_code(int idx)
+{
+       if (showbranch_use_color)
+               return column_colors[idx];
+       return "";
+}
+
+static const char *get_color_reset_code(void)
+{
+       if (showbranch_use_color)
+               return GIT_COLOR_RESET;
+       return "";
+}
+
 static struct commit *interesting(struct commit_list *list)
 {
        while (list) {
@@ -545,7 +574,12 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       if (!strcmp(var, "color.showbranch")) {
+               showbranch_use_color = git_config_colorbool(var, value, -1);
+               return 0;
+       }
+
+       return git_color_default_config(var, value, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
@@ -569,18 +603,25 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
        return 0;
 }
 
-static void parse_reflog_param(const char *arg, int *cnt, const char **base)
+static int reflog = 0;
+
+static int parse_reflog_param(const struct option *opt, const char *arg,
+                             int unset)
 {
        char *ep;
-       *cnt = strtoul(arg, &ep, 10);
+       const char **base = (const char **)opt->value;
+       if (!arg)
+               arg = "";
+       reflog = strtoul(arg, &ep, 10);
        if (*ep == ',')
                *base = ep + 1;
        else if (*ep)
-               die("unrecognized reflog param '%s'", arg);
+               return error("unrecognized reflog param '%s'", arg);
        else
                *base = NULL;
-       if (*cnt <= 0)
-               *cnt = DEFAULT_REFLOG;
+       if (reflog <= 0)
+               reflog = DEFAULT_REFLOG;
+       return 0;
 }
 
 int cmd_show_branch(int ac, const char **av, const char *prefix)
@@ -606,70 +647,67 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        int head_at = -1;
        int topics = 0;
        int dense = 1;
-       int reflog = 0;
        const char *reflog_base = NULL;
+       struct option builtin_show_branch_options[] = {
+               OPT_BOOLEAN('a', "all", &all_heads,
+                           "show remote-tracking and local branches"),
+               OPT_BOOLEAN('r', "remotes", &all_remotes,
+                           "show remote-tracking branches"),
+               OPT_BOOLEAN(0, "color", &showbranch_use_color,
+                           "color '*!+-' corresponding to the branch"),
+               { OPTION_INTEGER, 0, "more", &extra, "n",
+                           "show <n> more commits after the common ancestor",
+                           PARSE_OPT_OPTARG, NULL, (intptr_t)1 },
+               OPT_SET_INT(0, "list", &extra, "synonym to more=-1", -1),
+               OPT_BOOLEAN(0, "no-name", &no_name, "suppress naming strings"),
+               OPT_BOOLEAN(0, "current", &with_current_branch,
+                           "include the current branch"),
+               OPT_BOOLEAN(0, "sha1-name", &sha1_name,
+                           "name commits with their object names"),
+               OPT_BOOLEAN(0, "merge-base", &merge_base,
+                           "act like git merge-base -a"),
+               OPT_BOOLEAN(0, "independent", &independent,
+                           "show refs unreachable from any other ref"),
+               OPT_BOOLEAN(0, "topo-order", &lifo,
+                           "show commits in topological order"),
+               OPT_BOOLEAN(0, "topics", &topics,
+                           "show only commits not on the first branch"),
+               OPT_SET_INT(0, "sparse", &dense,
+                           "show merges reachable from only one tip", 0),
+               OPT_SET_INT(0, "date-order", &lifo,
+                           "show commits where no parent comes before its "
+                           "children", 0),
+               { OPTION_CALLBACK, 'g', "reflog", &reflog_base, "<n>[,<base>]",
+                           "show <n> most recent ref-log entries starting at "
+                           "base",
+                           PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP,
+                           parse_reflog_param },
+               OPT_END()
+       };
 
        git_config(git_show_branch_config, NULL);
 
+       if (showbranch_use_color == -1)
+               showbranch_use_color = git_use_color_default;
+
        /* If nothing is specified, try the default first */
        if (ac == 1 && default_num) {
                ac = default_num + 1;
                av = default_arg - 1; /* ick; we would not address av[0] */
        }
 
-       while (1 < ac && av[1][0] == '-') {
-               const char *arg = av[1];
-               if (!strcmp(arg, "--")) {
-                       ac--; av++;
-                       break;
-               }
-               else if (!strcmp(arg, "--all") || !strcmp(arg, "-a"))
-                       all_heads = all_remotes = 1;
-               else if (!strcmp(arg, "--remotes") || !strcmp(arg, "-r"))
-                       all_remotes = 1;
-               else if (!strcmp(arg, "--more"))
-                       extra = 1;
-               else if (!strcmp(arg, "--list"))
-                       extra = -1;
-               else if (!strcmp(arg, "--no-name"))
-                       no_name = 1;
-               else if (!strcmp(arg, "--current"))
-                       with_current_branch = 1;
-               else if (!strcmp(arg, "--sha1-name"))
-                       sha1_name = 1;
-               else if (!prefixcmp(arg, "--more="))
-                       extra = atoi(arg + 7);
-               else if (!strcmp(arg, "--merge-base"))
-                       merge_base = 1;
-               else if (!strcmp(arg, "--independent"))
-                       independent = 1;
-               else if (!strcmp(arg, "--topo-order"))
-                       lifo = 1;
-               else if (!strcmp(arg, "--topics"))
-                       topics = 1;
-               else if (!strcmp(arg, "--sparse"))
-                       dense = 0;
-               else if (!strcmp(arg, "--date-order"))
-                       lifo = 0;
-               else if (!strcmp(arg, "--reflog") || !strcmp(arg, "-g")) {
-                       reflog = DEFAULT_REFLOG;
-               }
-               else if (!prefixcmp(arg, "--reflog="))
-                       parse_reflog_param(arg + 9, &reflog, &reflog_base);
-               else if (!prefixcmp(arg, "-g="))
-                       parse_reflog_param(arg + 3, &reflog, &reflog_base);
-               else
-                       usage(show_branch_usage);
-               ac--; av++;
-       }
-       ac--; av++;
+       ac = parse_options(ac, av, prefix, builtin_show_branch_options,
+                          show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+       if (all_heads)
+               all_remotes = 1;
 
        if (extra || reflog) {
                /* "listing" mode is incompatible with
                 * independent nor merge-base modes.
                 */
                if (independent || merge_base)
-                       usage(show_branch_usage);
+                       usage_with_options(show_branch_usage,
+                                          builtin_show_branch_options);
                if (reflog && ((0 < extra) || all_heads || all_remotes))
                        /*
                         * Asking for --more in reflog mode does not
@@ -677,7 +715,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                         *
                         * Also --all and --remotes do not make sense either.
                         */
-                       usage(show_branch_usage_reflog);
+                       die("--reflog is incompatible with --all, --remotes, "
+                           "--independent or --merge-base");
        }
 
        /* If nothing is specified, show all branches by default */
@@ -843,8 +882,10 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        else {
                                for (j = 0; j < i; j++)
                                        putchar(' ');
-                               printf("%c [%s] ",
-                                      is_head ? '*' : '!', ref_name[i]);
+                               printf("%s%c%s [%s] ",
+                                      get_color_code(i % COLUMN_COLORS_MAX),
+                                      is_head ? '*' : '!',
+                                      get_color_reset_code(), ref_name[i]);
                        }
 
                        if (!reflog) {
@@ -903,7 +944,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                        mark = '*';
                                else
                                        mark = '+';
-                               putchar(mark);
+                               printf("%s%c%s",
+                                      get_color_code(i % COLUMN_COLORS_MAX),
+                                      mark, get_color_reset_code());
                        }
                        putchar(' ');
                }
index 6ae6bcc0e8d02d9af8a81a7d694c0bfd2c6c0514..ca855a5eb239f4dadccd53369e38db4e78b1d13f 100644 (file)
@@ -36,7 +36,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
        };
 
        git_config(git_default_config, NULL);
-       argc = parse_options(argc, argv, options, git_symbolic_ref_usage, 0);
+       argc = parse_options(argc, argv, prefix, options,
+                            git_symbolic_ref_usage, 0);
        if (msg &&!*msg)
                die("Refusing to perform update with empty message");
        switch (argc) {
index e54443009420d98dc281285e997b32f2a263b996..080e04a8fc6fc1fc3f10f59a4c1f1aa067b81aa1 100644 (file)
@@ -376,8 +376,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        struct commit_list *with_commit = NULL;
        struct option options[] = {
                OPT_BOOLEAN('l', NULL, &list, "list tag names"),
-               { OPTION_INTEGER, 'n', NULL, &lines, NULL,
-                               "print n lines of each tag message",
+               { OPTION_INTEGER, 'n', NULL, &lines, "n",
+                               "print <n> lines of each tag message",
                                PARSE_OPT_OPTARG, NULL, 1 },
                OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
                OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
@@ -387,7 +387,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                                        "annotated tag, needs a message"),
                OPT_CALLBACK('m', NULL, &msg, "msg",
                             "message for the tag", parse_msg_arg),
-               OPT_STRING('F', NULL, &msgfile, "file", "message in a file"),
+               OPT_FILENAME('F', NULL, &msgfile, "message in a file"),
                OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
                OPT_STRING('u', NULL, &keyid, "key-id",
                                        "use another key to sign the tag"),
@@ -405,8 +405,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
        git_config(git_tag_config, NULL);
 
-       argc = parse_options(argc, argv, options, git_tag_usage, 0);
-       msgfile = parse_options_fix_filename(prefix, msgfile);
+       argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
 
        if (keyid) {
                sign = 1;
index 9a773239cabab9998bcea829c0fb2abea9bdb8e8..8e831be476b71eed151cee1b5f2fc81c4edcb5ff 100644 (file)
@@ -422,8 +422,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
 static void unpack_one(unsigned nr)
 {
        unsigned shift;
-       unsigned char *pack, c;
-       unsigned long size;
+       unsigned char *pack;
+       unsigned long size, c;
        enum object_type type;
 
        obj_list[nr].offset = consumed_bytes;
index 378dc1b7a6bb4d56d301a34a4d44dae2f9a37e44..76ba1d5881b3cddc527bd363dec340c83dde605f 100644 (file)
@@ -23,7 +23,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        };
 
        git_config(git_default_config, NULL);
-       argc = parse_options(argc, argv, options, git_update_ref_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, git_update_ref_usage,
+                            0);
        if (msg && !*msg)
                die("Refusing to perform update with empty message.");
 
index 0206b416cbf08ae4c1b0d753784fb0b61bac46a1..c4cd1e1327f18ae27b74fac6dfd41938cd9dfe5d 100644 (file)
@@ -80,16 +80,17 @@ static void error_clnt(const char *fmt, ...)
        die("sent error to the client: %s", buf);
 }
 
-static void process_input(int child_fd, int band)
+static ssize_t process_input(int child_fd, int band)
 {
        char buf[16384];
        ssize_t sz = read(child_fd, buf, sizeof(buf));
        if (sz < 0) {
                if (errno != EAGAIN && errno != EINTR)
                        error_clnt("read error: %s\n", strerror(errno));
-               return;
+               return sz;
        }
        send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
+       return sz;
 }
 
 int cmd_upload_archive(int argc, const char **argv, const char *prefix)
@@ -131,6 +132,7 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
 
        while (1) {
                struct pollfd pfd[2];
+               ssize_t processed[2] = { 0, 0 };
                int status;
 
                pfd[0].fd = fd1[0];
@@ -147,12 +149,12 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
                }
                if (pfd[0].revents & POLLIN)
                        /* Data stream ready */
-                       process_input(pfd[0].fd, 1);
+                       processed[0] = process_input(pfd[0].fd, 1);
                if (pfd[1].revents & POLLIN)
                        /* Status stream ready */
-                       process_input(pfd[1].fd, 2);
+                       processed[1] = process_input(pfd[1].fd, 2);
                /* Always finish to read data when available */
-               if ((pfd[0].revents | pfd[1].revents) & POLLIN)
+               if (processed[0] || processed[1])
                        continue;
 
                if (waitpid(writer, &status, 0) < 0)
index 425ff8e89b361c34b3336cda58794682c66b57f3..20427d2963fa25c1177bd05ab9f8be2890b9f7ba 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -72,6 +72,7 @@ extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
+extern int cmd_mktree(int argc, const char **argv, const char *prefix);
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
index d0dd818b31faa04caa4d418a39ad3020a919aa2d..e4b2aa9c4a2a7fb52121828343aa27ab47d17279 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -98,7 +98,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
         */
        struct ref_list *p = &header->prerequisites;
        struct rev_info revs;
-       const char *argv[] = {NULL, "--all"};
+       const char *argv[] = {NULL, "--all", NULL};
        struct object_array refs;
        struct commit *commit;
        int i, ret = 0, req_nr;
diff --git a/cache.h b/cache.h
index b8503ad91c3b13ccaf87a6f596d13918f7ae114b..871c9844e88c8e73aa1bab0e079d0399bef1b11d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -614,6 +614,8 @@ extern int is_empty_blob_sha1(const unsigned char *sha1);
 
 int git_mkstemp(char *path, size_t n, const char *template);
 
+int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
+
 /*
  * NOTE NOTE NOTE!!
  *
diff --git a/compat/basename.c b/compat/basename.c
new file mode 100644 (file)
index 0000000..d8f8a3c
--- /dev/null
@@ -0,0 +1,15 @@
+#include "../git-compat-util.h"
+
+/* Adapted from libiberty's basename.c.  */
+char *gitbasename (char *path)
+{
+       const char *base;
+       /* Skip over the disk name in MSDOS pathnames. */
+       if (has_dos_drive_prefix(path))
+               path += 2;
+       for (base = path; *path; path++) {
+               if (is_dir_sep(*path))
+                       base = path + 1;
+       }
+       return (char *)base;
+}
index cdeda1d9859fd545950e0c39ee7de3c9dc09bb07..bed417875e1344690977fd6aa41b955f127b74b4 100644 (file)
@@ -1,5 +1,6 @@
 #include "../git-compat-util.h"
 #include "win32.h"
+#include <conio.h>
 #include "../strbuf.h"
 
 unsigned int _CRT_fmode = _O_BINARY;
@@ -525,8 +526,8 @@ static const char *parse_interpreter(const char *cmd)
        if (buf[0] != '#' || buf[1] != '!')
                return NULL;
        buf[n] = '\0';
-       p = strchr(buf, '\n');
-       if (!p)
+       p = buf + strcspn(buf, "\r\n");
+       if (!*p)
                return NULL;
 
        *p = '\0';
@@ -1156,3 +1157,77 @@ int link(const char *oldpath, const char *newpath)
        }
        return 0;
 }
+
+char *getpass(const char *prompt)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       fputs(prompt, stderr);
+       for (;;) {
+               char c = _getch();
+               if (c == '\r' || c == '\n')
+                       break;
+               strbuf_addch(&buf, c);
+       }
+       fputs("\n", stderr);
+       return strbuf_detach(&buf, NULL);
+}
+
+#ifndef NO_MINGW_REPLACE_READDIR
+/* MinGW readdir implementation to avoid extra lstats for Git */
+struct mingw_DIR
+{
+       struct _finddata_t      dd_dta;         /* disk transfer area for this dir */
+       struct mingw_dirent     dd_dir;         /* Our own implementation, including d_type */
+       long                    dd_handle;      /* _findnext handle */
+       int                     dd_stat;        /* 0 = next entry to read is first entry, -1 = off the end, positive = 0 based index of next entry */
+       char                    dd_name[1];     /* given path for dir with search pattern (struct is extended) */
+};
+
+struct dirent *mingw_readdir(DIR *dir)
+{
+       WIN32_FIND_DATAA buf;
+       HANDLE handle;
+       struct mingw_DIR *mdir = (struct mingw_DIR*)dir;
+
+       if (!dir->dd_handle) {
+               errno = EBADF; /* No set_errno for mingw */
+               return NULL;
+       }
+
+       if (dir->dd_handle == (long)INVALID_HANDLE_VALUE && dir->dd_stat == 0)
+       {
+               handle = FindFirstFileA(dir->dd_name, &buf);
+               DWORD lasterr = GetLastError();
+               dir->dd_handle = (long)handle;
+               if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
+                       errno = err_win_to_posix(lasterr);
+                       return NULL;
+               }
+       } else if (dir->dd_handle == (long)INVALID_HANDLE_VALUE) {
+               return NULL;
+       } else if (!FindNextFileA((HANDLE)dir->dd_handle, &buf)) {
+               DWORD lasterr = GetLastError();
+               FindClose((HANDLE)dir->dd_handle);
+               dir->dd_handle = (long)INVALID_HANDLE_VALUE;
+               /* POSIX says you shouldn't set errno when readdir can't
+                  find any more files; so, if another error we leave it set. */
+               if (lasterr != ERROR_NO_MORE_FILES)
+                       errno = err_win_to_posix(lasterr);
+               return NULL;
+       }
+
+       /* We get here if `buf' contains valid data.  */
+       strcpy(dir->dd_dir.d_name, buf.cFileName);
+       ++dir->dd_stat;
+
+       /* Set file type, based on WIN32_FIND_DATA */
+       mdir->dd_dir.d_type = 0;
+       if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               mdir->dd_dir.d_type |= DT_DIR;
+       else
+               mdir->dd_dir.d_type |= DT_REG;
+
+       return (struct dirent*)&dir->dd_dir;
+}
+#endif // !NO_MINGW_REPLACE_READDIR
index 762eb143a7654480d8831a6258a0767c0f33900b..4f7ba4c13f9d0086acf9d1cfadf6912b1d641033 100644 (file)
@@ -38,6 +38,8 @@ struct passwd {
        char *pw_dir;
 };
 
+extern char *getpass(const char *prompt);
+
 struct pollfd {
        int fd;           /* file descriptor */
        short events;     /* requested events */
@@ -109,7 +111,7 @@ static inline int mingw_unlink(const char *pathname)
 }
 #define unlink mingw_unlink
 
-static inline int waitpid(pid_t pid, unsigned *status, unsigned options)
+static inline int waitpid(pid_t pid, int *status, unsigned options)
 {
        if (options == 0)
                return _cwait(status, pid, 0);
@@ -233,3 +235,32 @@ int main(int argc, const char **argv) \
        return mingw_main(argc, argv); \
 } \
 static int mingw_main(c,v)
+
+#ifndef NO_MINGW_REPLACE_READDIR
+/*
+ * A replacement of readdir, to ensure that it reads the file type at
+ * the same time. This avoid extra unneeded lstats in git on MinGW
+ */
+#undef DT_UNKNOWN
+#undef DT_DIR
+#undef DT_REG
+#undef DT_LNK
+#define DT_UNKNOWN     0
+#define DT_DIR         1
+#define DT_REG         2
+#define DT_LNK         3
+
+struct mingw_dirent
+{
+       long            d_ino;                  /* Always zero. */
+       union {
+               unsigned short  d_reclen;       /* Always zero. */
+               unsigned char   d_type;         /* Reimplementation adds this */
+       };
+       unsigned short  d_namlen;               /* Length of name in d_name. */
+       char            d_name[FILENAME_MAX];   /* File name. */
+};
+#define dirent mingw_dirent
+#define readdir(x) mingw_readdir(x)
+struct dirent *mingw_readdir(DIR *dir);
+#endif // !NO_MINGW_REPLACE_READDIR
diff --git a/compat/mkstemps.c b/compat/mkstemps.c
new file mode 100644 (file)
index 0000000..14179c8
--- /dev/null
@@ -0,0 +1,70 @@
+#include "../git-compat-util.h"
+
+/* Adapted from libiberty's mkstemp.c. */
+
+#undef TMP_MAX
+#define TMP_MAX 16384
+
+int gitmkstemps(char *pattern, int suffix_len)
+{
+       static const char letters[] =
+               "abcdefghijklmnopqrstuvwxyz"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+               "0123456789";
+       static const int num_letters = 62;
+       uint64_t value;
+       struct timeval tv;
+       char *template;
+       size_t len;
+       int fd, count;
+
+       len = strlen(pattern);
+
+       if (len < 6 + suffix_len) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /*
+        * Replace pattern's XXXXXX characters with randomness.
+        * Try TMP_MAX different filenames.
+        */
+       gettimeofday(&tv, NULL);
+       value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+       template = &pattern[len - 6 - suffix_len];
+       for (count = 0; count < TMP_MAX; ++count) {
+               uint64_t v = value;
+               /* Fill in the random bits. */
+               template[0] = letters[v % num_letters]; v /= num_letters;
+               template[1] = letters[v % num_letters]; v /= num_letters;
+               template[2] = letters[v % num_letters]; v /= num_letters;
+               template[3] = letters[v % num_letters]; v /= num_letters;
+               template[4] = letters[v % num_letters]; v /= num_letters;
+               template[5] = letters[v % num_letters]; v /= num_letters;
+
+               fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, 0600);
+               if (fd > 0)
+                       return fd;
+               /*
+                * Fatal error (EPERM, ENOSPC etc).
+                * It doesn't make sense to loop.
+                */
+               if (errno != EEXIST)
+                       break;
+               /*
+                * This is a random value.  It is only necessary that
+                * the next TMP_MAX values generated by adding 7777 to
+                * VALUE are different with (module 2^32).
+                */
+               value += 7777;
+       }
+       /* We return the null string if we can't find a unique file name.  */
+       pattern[0] = '\0';
+       errno = EINVAL;
+       return -1;
+}
diff --git a/compat/nedmalloc/License.txt b/compat/nedmalloc/License.txt
new file mode 100644 (file)
index 0000000..36b7cd9
--- /dev/null
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/compat/nedmalloc/Readme.txt b/compat/nedmalloc/Readme.txt
new file mode 100644 (file)
index 0000000..8763656
--- /dev/null
@@ -0,0 +1,136 @@
+nedalloc v1.05 15th June 2008:
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+by Niall Douglas (http://www.nedprod.com/programs/portable/nedmalloc/)
+
+Enclosed is nedalloc, an alternative malloc implementation for multiple
+threads without lock contention based on dlmalloc v2.8.4. It is more
+or less a newer implementation of ptmalloc2, the standard allocator in
+Linux (which is based on dlmalloc v2.7.0) but also contains a per-thread
+cache for maximum CPU scalability.
+
+It is licensed under the Boost Software License which basically means
+you can do anything you like with it. This does not apply to the malloc.c.h
+file which remains copyright to others.
+
+It has been tested on win32 (x86), win64 (x64), Linux (x64), FreeBSD (x64)
+and Apple MacOS X (x86). It works very well on all of these and is very
+significantly faster than the system allocator on all of these platforms.
+
+By literally dropping in this allocator as a replacement for your system
+allocator, you can see real world improvements of up to three times in normal
+code!
+
+To use:
+-=-=-=-
+Drop in nedmalloc.h, nedmalloc.c and malloc.c.h into your project.
+Configure using the instructions in nedmalloc.h. Run and enjoy.
+
+To test, compile test.c. It will run a comparison between your system
+allocator and nedalloc and tell you how much faster nedalloc is. It also
+serves as an example of usage.
+
+Notes:
+-=-=-=
+If you want the very latest version of this allocator, get it from the
+TnFOX SVN repository at svn://svn.berlios.de/viewcvs/tnfox/trunk/src/nedmalloc
+
+Because of how nedalloc allocates an mspace per thread, it can cause
+severe bloating of memory usage under certain allocation patterns.
+You can substantially reduce this wastage by setting MAXTHREADSINPOOL
+or the threads parameter to nedcreatepool() to a fraction of the number of
+threads which would normally be in a pool at once. This will reduce
+bloating at the cost of an increase in lock contention. If allocated size
+is less than THREADCACHEMAX, locking is avoided 90-99% of the time and
+if most of your allocations are below this value, you can safely set
+MAXTHREADSINPOOL to one.
+
+You will suffer memory leakage unless you call neddisablethreadcache()
+per pool for every thread which exits. This is because nedalloc cannot
+portably know when a thread exits and thus when its thread cache can
+be returned for use by other code. Don't forget pool zero, the system pool.
+
+For C++ type allocation patterns (where the same sizes of memory are
+regularly allocated and deallocated as objects are created and destroyed),
+the threadcache always benefits performance. If however your allocation
+patterns are different, searching the threadcache may significantly slow
+down your code - as a rule of thumb, if cache utilisation is below 80%
+(see the source for neddisablethreadcache() for how to enable debug
+printing in release mode) then you should disable the thread cache for
+that thread. You can compile out the threadcache code by setting
+THREADCACHEMAX to zero.
+
+Speed comparisons:
+-=-=-=-=-=-=-=-=-=
+See Benchmarks.xls for details.
+
+The enclosed test.c can do two things: it can be a torture test or a speed
+test. The speed test is designed to be a representative synthetic
+memory allocator test. It works by randomly mixing allocations with frees
+with half of the allocation sizes being a two power multiple less than
+512 bytes (to mimic C++ stack instantiated objects) and the other half
+being a simple random value less than 16Kb.
+
+The real world code results are from Tn's TestIO benchmark. This is a
+heavily multithreaded and memory intensive benchmark with a lot of branching
+and other stuff modern processors don't like so much. As you'll note, the
+test doesn't show the benefits of the threadcache mostly due to the saturation
+of the memory bus being the limiting factor.
+
+ChangeLog:
+-=-=-=-=-=
+v1.05 15th June 2008:
+ * { 1042 } Added error check for TLSSET() and TLSFREE() macros. Thanks to
+Markus Elfring for reporting this.
+ * { 1043 } Fixed a segfault when freeing memory allocated using
+nedindependent_comalloc(). Thanks to Pavel Vozenilek for reporting this.
+
+v1.04 14th July 2007:
+ * Fixed a bug with the new optimised implementation that failed to lock
+on a realloc under certain conditions.
+ * Fixed lack of thread synchronisation in InitPool() causing pool corruption
+ * Fixed a memory leak of thread cache contents on disabling. Thanks to Earl
+Chew for reporting this.
+ * Added a sanity check for freed blocks being valid.
+ * Reworked test.c into being a torture test.
+ * Fixed GCC assembler optimisation misspecification
+
+v1.04alpha_svn915 7th October 2006:
+ * Fixed failure to unlock thread cache list if allocating a new list failed.
+Thanks to Dmitry Chichkov for reporting this. Futher thanks to Aleksey Sanin.
+ * Fixed realloc(0, <size>) segfaulting. Thanks to Dmitry Chichkov for
+reporting this.
+ * Made config defines #ifndef so they can be overriden by the build system.
+Thanks to Aleksey Sanin for suggesting this.
+ * Fixed deadlock in nedprealloc() due to unnecessary locking of preferred
+thread mspace when mspace_realloc() always uses the original block's mspace
+anyway. Thanks to Aleksey Sanin for reporting this.
+ * Made some speed improvements by hacking mspace_malloc() to no longer lock
+its mspace, thus allowing the recursive mutex implementation to be removed
+with an associated speed increase. Thanks to Aleksey Sanin for suggesting this.
+ * Fixed a bug where allocating mspaces overran its max limit. Thanks to
+Aleksey Sanin for reporting this.
+
+v1.03 10th July 2006:
+ * Fixed memory corruption bug in threadcache code which only appeared with >4
+threads and in heavy use of the threadcache.
+
+v1.02 15th May 2006:
+ * Integrated dlmalloc v2.8.4, fixing the win32 memory release problem and
+improving performance still further. Speed is now up to twice the speed of v1.01
+(average is 67% faster).
+ * Fixed win32 critical section implementation. Thanks to Pavel Kuznetsov
+for reporting this.
+ * Wasn't locking mspace if all mspaces were locked. Thanks to Pavel Kuznetsov
+for reporting this.
+ * Added Apple Mac OS X support.
+
+v1.01 24th February 2006:
+ * Fixed multiprocessor scaling problems by removing sources of cache sloshing
+ * Earl Chew <earl_chew <at> agilent <dot> com> sent patches for the following:
+   1. size2binidx() wasn't working for default code path (non x86)
+   2. Fixed failure to release mspace lock under certain circumstances which
+      caused a deadlock
+
+v1.00 1st January 2006:
+ * First release
diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h
new file mode 100644 (file)
index 0000000..74c42e3
--- /dev/null
@@ -0,0 +1,5752 @@
+/*
+  This is a version (aka dlmalloc) of malloc/free/realloc written by
+  Doug Lea and released to the public domain, as explained at
+  http://creativecommons.org/licenses/publicdomain.  Send questions,
+  comments, complaints, performance data, etc to dl@cs.oswego.edu
+
+* Version pre-2.8.4 Mon Nov 27 11:22:37 2006    (dl at gee)
+
+   Note: There may be an updated version of this malloc obtainable at
+          ftp://gee.cs.oswego.edu/pub/misc/malloc.c
+        Check before installing!
+
+* Quickstart
+
+  This library is all in one file to simplify the most common usage:
+  ftp it, compile it (-O3), and link it into another program. All of
+  the compile-time options default to reasonable values for use on
+  most platforms.  You might later want to step through various
+  compile-time and dynamic tuning options.
+
+  For convenience, an include file for code using this malloc is at:
+     ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.4.h
+  You don't really need this .h file unless you call functions not
+  defined in your system include files.  The .h file contains only the
+  excerpts from this file needed for using this malloc on ANSI C/C++
+  systems, so long as you haven't changed compile-time options about
+  naming and tuning parameters.  If you do, then you can create your
+  own malloc.h that does include all settings by cutting at the point
+  indicated below. Note that you may already by default be using a C
+  library containing a malloc that is based on some version of this
+  malloc (for example in linux). You might still want to use the one
+  in this file to customize settings or to avoid overheads associated
+  with library versions.
+
+* Vital statistics:
+
+  Supported pointer/size_t representation:       4 or 8 bytes
+       size_t MUST be an unsigned type of the same width as
+       pointers. (If you are using an ancient system that declares
+       size_t as a signed type, or need it to be a different width
+       than pointers, you can use a previous release of this malloc
+       (e.g. 2.7.2) supporting these.)
+
+  Alignment:                                     8 bytes (default)
+       This suffices for nearly all current machines and C compilers.
+       However, you can define MALLOC_ALIGNMENT to be wider than this
+       if necessary (up to 128bytes), at the expense of using more space.
+
+  Minimum overhead per allocated chunk:   4 or  8 bytes (if 4byte sizes)
+                                         8 or 16 bytes (if 8byte sizes)
+       Each malloced chunk has a hidden word of overhead holding size
+       and status information, and additional cross-check word
+       if FOOTERS is defined.
+
+  Minimum allocated size: 4-byte ptrs:  16 bytes    (including overhead)
+                         8-byte ptrs:  32 bytes    (including overhead)
+
+       Even a request for zero bytes (i.e., malloc(0)) returns a
+       pointer to something of the minimum allocatable size.
+       The maximum overhead wastage (i.e., number of extra bytes
+       allocated than were requested in malloc) is less than or equal
+       to the minimum size, except for requests >= mmap_threshold that
+       are serviced via mmap(), where the worst case wastage is about
+       32 bytes plus the remainder from a system page (the minimal
+       mmap unit); typically 4096 or 8192 bytes.
+
+  Security: static-safe; optionally more or less
+       The "security" of malloc refers to the ability of malicious
+       code to accentuate the effects of errors (for example, freeing
+       space that is not currently malloc'ed or overwriting past the
+       ends of chunks) in code that calls malloc.  This malloc
+       guarantees not to modify any memory locations below the base of
+       heap, i.e., static variables, even in the presence of usage
+       errors.  The routines additionally detect most improper frees
+       and reallocs.  All this holds as long as the static bookkeeping
+       for malloc itself is not corrupted by some other means.  This
+       is only one aspect of security -- these checks do not, and
+       cannot, detect all possible programming errors.
+
+       If FOOTERS is defined nonzero, then each allocated chunk
+       carries an additional check word to verify that it was malloced
+       from its space.  These check words are the same within each
+       execution of a program using malloc, but differ across
+       executions, so externally crafted fake chunks cannot be
+       freed. This improves security by rejecting frees/reallocs that
+       could corrupt heap memory, in addition to the checks preventing
+       writes to statics that are always on.  This may further improve
+       security at the expense of time and space overhead.  (Note that
+       FOOTERS may also be worth using with MSPACES.)
+
+       By default detected errors cause the program to abort (calling
+       "abort()"). You can override this to instead proceed past
+       errors by defining PROCEED_ON_ERROR.  In this case, a bad free
+       has no effect, and a malloc that encounters a bad address
+       caused by user overwrites will ignore the bad address by
+       dropping pointers and indices to all known memory. This may
+       be appropriate for programs that should continue if at all
+       possible in the face of programming errors, although they may
+       run out of memory because dropped memory is never reclaimed.
+
+       If you don't like either of these options, you can define
+       CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything
+       else. And if if you are sure that your program using malloc has
+       no errors or vulnerabilities, you can define INSECURE to 1,
+       which might (or might not) provide a small performance improvement.
+
+  Thread-safety: NOT thread-safe unless USE_LOCKS defined
+       When USE_LOCKS is defined, each public call to malloc, free,
+       etc is surrounded with either a pthread mutex or a win32
+       spinlock (depending on WIN32). This is not especially fast, and
+       can be a major bottleneck.  It is designed only to provide
+       minimal protection in concurrent environments, and to provide a
+       basis for extensions.  If you are using malloc in a concurrent
+       program, consider instead using nedmalloc
+       (http://www.nedprod.com/programs/portable/nedmalloc/) or
+       ptmalloc (See http://www.malloc.de), which are derived
+       from versions of this malloc.
+
+  System requirements: Any combination of MORECORE and/or MMAP/MUNMAP
+       This malloc can use unix sbrk or any emulation (invoked using
+       the CALL_MORECORE macro) and/or mmap/munmap or any emulation
+       (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system
+       memory.  On most unix systems, it tends to work best if both
+       MORECORE and MMAP are enabled.  On Win32, it uses emulations
+       based on VirtualAlloc. It also uses common C library functions
+       like memset.
+
+  Compliance: I believe it is compliant with the Single Unix Specification
+       (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably
+       others as well.
+
+* Overview of algorithms
+
+  This is not the fastest, most space-conserving, most portable, or
+  most tunable malloc ever written. However it is among the fastest
+  while also being among the most space-conserving, portable and
+  tunable.  Consistent balance across these factors results in a good
+  general-purpose allocator for malloc-intensive programs.
+
+  In most ways, this malloc is a best-fit allocator. Generally, it
+  chooses the best-fitting existing chunk for a request, with ties
+  broken in approximately least-recently-used order. (This strategy
+  normally maintains low fragmentation.) However, for requests less
+  than 256bytes, it deviates from best-fit when there is not an
+  exactly fitting available chunk by preferring to use space adjacent
+  to that used for the previous small request, as well as by breaking
+  ties in approximately most-recently-used order. (These enhance
+  locality of series of small allocations.)  And for very large requests
+  (>= 256Kb by default), it relies on system memory mapping
+  facilities, if supported.  (This helps avoid carrying around and
+  possibly fragmenting memory used only for large chunks.)
+
+  All operations (except malloc_stats and mallinfo) have execution
+  times that are bounded by a constant factor of the number of bits in
+  a size_t, not counting any clearing in calloc or copying in realloc,
+  or actions surrounding MORECORE and MMAP that have times
+  proportional to the number of non-contiguous regions returned by
+  system allocation routines, which is often just 1. In real-time
+  applications, you can optionally suppress segment traversals using
+  NO_SEGMENT_TRAVERSAL, which assures bounded execution even when
+  system allocators return non-contiguous spaces, at the typical
+  expense of carrying around more memory and increased fragmentation.
+
+  The implementation is not very modular and seriously overuses
+  macros. Perhaps someday all C compilers will do as good a job
+  inlining modular code as can now be done by brute-force expansion,
+  but now, enough of them seem not to.
+
+  Some compilers issue a lot of warnings about code that is
+  dead/unreachable only on some platforms, and also about intentional
+  uses of negation on unsigned types. All known cases of each can be
+  ignored.
+
+  For a longer but out of date high-level description, see
+     http://gee.cs.oswego.edu/dl/html/malloc.html
+
+* MSPACES
+  If MSPACES is defined, then in addition to malloc, free, etc.,
+  this file also defines mspace_malloc, mspace_free, etc. These
+  are versions of malloc routines that take an "mspace" argument
+  obtained using create_mspace, to control all internal bookkeeping.
+  If ONLY_MSPACES is defined, only these versions are compiled.
+  So if you would like to use this allocator for only some allocations,
+  and your system malloc for others, you can compile with
+  ONLY_MSPACES and then do something like...
+    static mspace mymspace = create_mspace(0,0); // for example
+    #define mymalloc(bytes)  mspace_malloc(mymspace, bytes)
+
+  (Note: If you only need one instance of an mspace, you can instead
+  use "USE_DL_PREFIX" to relabel the global malloc.)
+
+  You can similarly create thread-local allocators by storing
+  mspaces as thread-locals. For example:
+    static __thread mspace tlms = 0;
+    void*  tlmalloc(size_t bytes) {
+      if (tlms == 0) tlms = create_mspace(0, 0);
+      return mspace_malloc(tlms, bytes);
+    }
+    void  tlfree(void* mem) { mspace_free(tlms, mem); }
+
+  Unless FOOTERS is defined, each mspace is completely independent.
+  You cannot allocate from one and free to another (although
+  conformance is only weakly checked, so usage errors are not always
+  caught). If FOOTERS is defined, then each chunk carries around a tag
+  indicating its originating mspace, and frees are directed to their
+  originating spaces.
+
+ -------------------------  Compile-time options ---------------------------
+
+Be careful in setting #define values for numerical constants of type
+size_t. On some systems, literal values are not automatically extended
+to size_t precision unless they are explicitly casted. You can also
+use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below.
+
+WIN32                    default: defined if _WIN32 defined
+  Defining WIN32 sets up defaults for MS environment and compilers.
+  Otherwise defaults are for unix. Beware that there seem to be some
+  cases where this malloc might not be a pure drop-in replacement for
+  Win32 malloc: Random-looking failures from Win32 GDI API's (eg;
+  SetDIBits()) may be due to bugs in some video driver implementations
+  when pixel buffers are malloc()ed, and the region spans more than
+  one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb)
+  default granularity, pixel buffers may straddle virtual allocation
+  regions more often than when using the Microsoft allocator.  You can
+  avoid this by using VirtualAlloc() and VirtualFree() for all pixel
+  buffers rather than using malloc().  If this is not possible,
+  recompile this malloc with a larger DEFAULT_GRANULARITY.
+
+MALLOC_ALIGNMENT         default: (size_t)8
+  Controls the minimum alignment for malloc'ed chunks.  It must be a
+  power of two and at least 8, even on machines for which smaller
+  alignments would suffice. It may be defined as larger than this
+  though. Note however that code and data structures are optimized for
+  the case of 8-byte alignment.
+
+MSPACES                  default: 0 (false)
+  If true, compile in support for independent allocation spaces.
+  This is only supported if HAVE_MMAP is true.
+
+ONLY_MSPACES             default: 0 (false)
+  If true, only compile in mspace versions, not regular versions.
+
+USE_LOCKS                default: 0 (false)
+  Causes each call to each public routine to be surrounded with
+  pthread or WIN32 mutex lock/unlock. (If set true, this can be
+  overridden on a per-mspace basis for mspace versions.) If set to a
+  non-zero value other than 1, locks are used, but their
+  implementation is left out, so lock functions must be supplied manually.
+
+USE_SPIN_LOCKS           default: 1 iff USE_LOCKS and on x86 using gcc or MSC
+  If true, uses custom spin locks for locking. This is currently
+  supported only for x86 platforms using gcc or recent MS compilers.
+  Otherwise, posix locks or win32 critical sections are used.
+
+FOOTERS                  default: 0
+  If true, provide extra checking and dispatching by placing
+  information in the footers of allocated chunks. This adds
+  space and time overhead.
+
+INSECURE                 default: 0
+  If true, omit checks for usage errors and heap space overwrites.
+
+USE_DL_PREFIX            default: NOT defined
+  Causes compiler to prefix all public routines with the string 'dl'.
+  This can be useful when you only want to use this malloc in one part
+  of a program, using your regular system malloc elsewhere.
+
+ABORT                    default: defined as abort()
+  Defines how to abort on failed checks.  On most systems, a failed
+  check cannot die with an "assert" or even print an informative
+  message, because the underlying print routines in turn call malloc,
+  which will fail again.  Generally, the best policy is to simply call
+  abort(). It's not very useful to do more than this because many
+  errors due to overwriting will show up as address faults (null, odd
+  addresses etc) rather than malloc-triggered checks, so will also
+  abort.  Also, most compilers know that abort() does not return, so
+  can better optimize code conditionally calling it.
+
+PROCEED_ON_ERROR           default: defined as 0 (false)
+  Controls whether detected bad addresses cause them to bypassed
+  rather than aborting. If set, detected bad arguments to free and
+  realloc are ignored. And all bookkeeping information is zeroed out
+  upon a detected overwrite of freed heap space, thus losing the
+  ability to ever return it from malloc again, but enabling the
+  application to proceed. If PROCEED_ON_ERROR is defined, the
+  static variable malloc_corruption_error_count is compiled in
+  and can be examined to see if errors have occurred. This option
+  generates slower code than the default abort policy.
+
+DEBUG                    default: NOT defined
+  The DEBUG setting is mainly intended for people trying to modify
+  this code or diagnose problems when porting to new platforms.
+  However, it may also be able to better isolate user errors than just
+  using runtime checks.  The assertions in the check routines spell
+  out in more detail the assumptions and invariants underlying the
+  algorithms.  The checking is fairly extensive, and will slow down
+  execution noticeably. Calling malloc_stats or mallinfo with DEBUG
+  set will attempt to check every non-mmapped allocated and free chunk
+  in the course of computing the summaries.
+
+ABORT_ON_ASSERT_FAILURE   default: defined as 1 (true)
+  Debugging assertion failures can be nearly impossible if your
+  version of the assert macro causes malloc to be called, which will
+  lead to a cascade of further failures, blowing the runtime stack.
+  ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(),
+  which will usually make debugging easier.
+
+MALLOC_FAILURE_ACTION     default: sets errno to ENOMEM, or no-op on win32
+  The action to take before "return 0" when malloc fails to be able to
+  return memory because there is none available.
+
+HAVE_MORECORE             default: 1 (true) unless win32 or ONLY_MSPACES
+  True if this system supports sbrk or an emulation of it.
+
+MORECORE                  default: sbrk
+  The name of the sbrk-style system routine to call to obtain more
+  memory.  See below for guidance on writing custom MORECORE
+  functions. The type of the argument to sbrk/MORECORE varies across
+  systems.  It cannot be size_t, because it supports negative
+  arguments, so it is normally the signed type of the same width as
+  size_t (sometimes declared as "intptr_t").  It doesn't much matter
+  though. Internally, we only call it with arguments less than half
+  the max value of a size_t, which should work across all reasonable
+  possibilities, although sometimes generating compiler warnings.
+
+MORECORE_CONTIGUOUS       default: 1 (true) if HAVE_MORECORE
+  If true, take advantage of fact that consecutive calls to MORECORE
+  with positive arguments always return contiguous increasing
+  addresses.  This is true of unix sbrk. It does not hurt too much to
+  set it true anyway, since malloc copes with non-contiguities.
+  Setting it false when definitely non-contiguous saves time
+  and possibly wasted space it would take to discover this though.
+
+MORECORE_CANNOT_TRIM      default: NOT defined
+  True if MORECORE cannot release space back to the system when given
+  negative arguments. This is generally necessary only if you are
+  using a hand-crafted MORECORE function that cannot handle negative
+  arguments.
+
+NO_SEGMENT_TRAVERSAL       default: 0
+  If non-zero, suppresses traversals of memory segments
+  returned by either MORECORE or CALL_MMAP. This disables
+  merging of segments that are contiguous, and selectively
+  releasing them to the OS if unused, but bounds execution times.
+
+HAVE_MMAP                 default: 1 (true)
+  True if this system supports mmap or an emulation of it.  If so, and
+  HAVE_MORECORE is not true, MMAP is used for all system
+  allocation. If set and HAVE_MORECORE is true as well, MMAP is
+  primarily used to directly allocate very large blocks. It is also
+  used as a backup strategy in cases where MORECORE fails to provide
+  space from system. Note: A single call to MUNMAP is assumed to be
+  able to unmap memory that may have be allocated using multiple calls
+  to MMAP, so long as they are adjacent.
+
+HAVE_MREMAP               default: 1 on linux, else 0
+  If true realloc() uses mremap() to re-allocate large blocks and
+  extend or shrink allocation spaces.
+
+MMAP_CLEARS               default: 1 except on WINCE.
+  True if mmap clears memory so calloc doesn't need to. This is true
+  for standard unix mmap using /dev/zero and on WIN32 except for WINCE.
+
+USE_BUILTIN_FFS            default: 0 (i.e., not used)
+  Causes malloc to use the builtin ffs() function to compute indices.
+  Some compilers may recognize and intrinsify ffs to be faster than the
+  supplied C version. Also, the case of x86 using gcc is special-cased
+  to an asm instruction, so is already as fast as it can be, and so
+  this setting has no effect. Similarly for Win32 under recent MS compilers.
+  (On most x86s, the asm version is only slightly faster than the C version.)
+
+malloc_getpagesize         default: derive from system includes, or 4096.
+  The system page size. To the extent possible, this malloc manages
+  memory from the system in page-size units.  This may be (and
+  usually is) a function rather than a constant. This is ignored
+  if WIN32, where page size is determined using getSystemInfo during
+  initialization.
+
+USE_DEV_RANDOM             default: 0 (i.e., not used)
+  Causes malloc to use /dev/random to initialize secure magic seed for
+  stamping footers. Otherwise, the current time is used.
+
+NO_MALLINFO                default: 0
+  If defined, don't compile "mallinfo". This can be a simple way
+  of dealing with mismatches between system declarations and
+  those in this file.
+
+MALLINFO_FIELD_TYPE        default: size_t
+  The type of the fields in the mallinfo struct. This was originally
+  defined as "int" in SVID etc, but is more usefully defined as
+  size_t. The value is used only if  HAVE_USR_INCLUDE_MALLOC_H is not set
+
+REALLOC_ZERO_BYTES_FREES    default: not defined
+  This should be set if a call to realloc with zero bytes should
+  be the same as a call to free. Some people think it should. Otherwise,
+  since this malloc returns a unique pointer for malloc(0), so does
+  realloc(p, 0).
+
+LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H
+LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H,  LACKS_ERRNO_H
+LACKS_STDLIB_H                default: NOT defined unless on WIN32
+  Define these if your system does not have these header files.
+  You might need to manually insert some of the declarations they provide.
+
+DEFAULT_GRANULARITY        default: page size if MORECORE_CONTIGUOUS,
+                               system_info.dwAllocationGranularity in WIN32,
+                               otherwise 64K.
+      Also settable using mallopt(M_GRANULARITY, x)
+  The unit for allocating and deallocating memory from the system.  On
+  most systems with contiguous MORECORE, there is no reason to
+  make this more than a page. However, systems with MMAP tend to
+  either require or encourage larger granularities.  You can increase
+  this value to prevent system allocation functions to be called so
+  often, especially if they are slow.  The value must be at least one
+  page and must be a power of two.  Setting to 0 causes initialization
+  to either page size or win32 region size.  (Note: In previous
+  versions of malloc, the equivalent of this option was called
+  "TOP_PAD")
+
+DEFAULT_TRIM_THRESHOLD    default: 2MB
+      Also settable using mallopt(M_TRIM_THRESHOLD, x)
+  The maximum amount of unused top-most memory to keep before
+  releasing via malloc_trim in free().  Automatic trimming is mainly
+  useful in long-lived programs using contiguous MORECORE.  Because
+  trimming via sbrk can be slow on some systems, and can sometimes be
+  wasteful (in cases where programs immediately afterward allocate
+  more large chunks) the value should be high enough so that your
+  overall system performance would improve by releasing this much
+  memory.  As a rough guide, you might set to a value close to the
+  average size of a process (program) running on your system.
+  Releasing this much memory would allow such a process to run in
+  memory.  Generally, it is worth tuning trim thresholds when a
+  program undergoes phases where several large chunks are allocated
+  and released in ways that can reuse each other's storage, perhaps
+  mixed with phases where there are no such chunks at all. The trim
+  value must be greater than page size to have any useful effect.  To
+  disable trimming completely, you can set to MAX_SIZE_T. Note that the trick
+  some people use of mallocing a huge space and then freeing it at
+  program startup, in an attempt to reserve system memory, doesn't
+  have the intended effect under automatic trimming, since that memory
+  will immediately be returned to the system.
+
+DEFAULT_MMAP_THRESHOLD       default: 256K
+      Also settable using mallopt(M_MMAP_THRESHOLD, x)
+  The request size threshold for using MMAP to directly service a
+  request. Requests of at least this size that cannot be allocated
+  using already-existing space will be serviced via mmap.  (If enough
+  normal freed space already exists it is used instead.)  Using mmap
+  segregates relatively large chunks of memory so that they can be
+  individually obtained and released from the host system. A request
+  serviced through mmap is never reused by any other request (at least
+  not directly; the system may just so happen to remap successive
+  requests to the same locations).  Segregating space in this way has
+  the benefits that: Mmapped space can always be individually released
+  back to the system, which helps keep the system level memory demands
+  of a long-lived program low.  Also, mapped memory doesn't become
+  `locked' between other chunks, as can happen with normally allocated
+  chunks, which means that even trimming via malloc_trim would not
+  release them.  However, it has the disadvantage that the space
+  cannot be reclaimed, consolidated, and then used to service later
+  requests, as happens with normal chunks.  The advantages of mmap
+  nearly always outweigh disadvantages for "large" chunks, but the
+  value of "large" may vary across systems.  The default is an
+  empirically derived value that works well in most systems. You can
+  disable mmap by setting to MAX_SIZE_T.
+
+MAX_RELEASE_CHECK_RATE   default: 4095 unless not HAVE_MMAP
+  The number of consolidated frees between checks to release
+  unused segments when freeing. When using non-contiguous segments,
+  especially with multiple mspaces, checking only for topmost space
+  doesn't always suffice to trigger trimming. To compensate for this,
+  free() will, with a period of MAX_RELEASE_CHECK_RATE (or the
+  current number of segments, if greater) try to release unused
+  segments to the OS when freeing chunks that result in
+  consolidation. The best value for this parameter is a compromise
+  between slowing down frees with relatively costly checks that
+  rarely trigger versus holding on to unused memory. To effectively
+  disable, set to MAX_SIZE_T. This may lead to a very slight speed
+  improvement at the expense of carrying around more memory.
+*/
+
+/* Version identifier to allow people to support multiple versions */
+#ifndef DLMALLOC_VERSION
+#define DLMALLOC_VERSION 20804
+#endif /* DLMALLOC_VERSION */
+
+#ifndef WIN32
+#ifdef _WIN32
+#define WIN32 1
+#endif  /* _WIN32 */
+#ifdef _WIN32_WCE
+#define LACKS_FCNTL_H
+#define WIN32 1
+#endif /* _WIN32_WCE */
+#endif  /* WIN32 */
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#define _WIN32_WINNT 0x403
+#include <windows.h>
+#define HAVE_MMAP 1
+#define HAVE_MORECORE 0
+#define LACKS_UNISTD_H
+#define LACKS_SYS_PARAM_H
+#define LACKS_SYS_MMAN_H
+#define LACKS_STRING_H
+#define LACKS_STRINGS_H
+#define LACKS_SYS_TYPES_H
+#define LACKS_ERRNO_H
+#ifndef MALLOC_FAILURE_ACTION
+#define MALLOC_FAILURE_ACTION
+#endif /* MALLOC_FAILURE_ACTION */
+#ifdef _WIN32_WCE /* WINCE reportedly does not clear */
+#define MMAP_CLEARS 0
+#else
+#define MMAP_CLEARS 1
+#endif /* _WIN32_WCE */
+#endif  /* WIN32 */
+
+#if defined(DARWIN) || defined(_DARWIN)
+/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */
+#ifndef HAVE_MORECORE
+#define HAVE_MORECORE 0
+#define HAVE_MMAP 1
+/* OSX allocators provide 16 byte alignment */
+#ifndef MALLOC_ALIGNMENT
+#define MALLOC_ALIGNMENT ((size_t)16U)
+#endif
+#endif  /* HAVE_MORECORE */
+#endif  /* DARWIN */
+
+#ifndef LACKS_SYS_TYPES_H
+#include <sys/types.h>  /* For size_t */
+#endif  /* LACKS_SYS_TYPES_H */
+
+/* The maximum possible size_t value has all bits set */
+#define MAX_SIZE_T           (~(size_t)0)
+
+#ifndef ONLY_MSPACES
+#define ONLY_MSPACES 0     /* define to a value */
+#else
+#define ONLY_MSPACES 1
+#endif  /* ONLY_MSPACES */
+#ifndef MSPACES
+#if ONLY_MSPACES
+#define MSPACES 1
+#else   /* ONLY_MSPACES */
+#define MSPACES 0
+#endif  /* ONLY_MSPACES */
+#endif  /* MSPACES */
+#ifndef MALLOC_ALIGNMENT
+#define MALLOC_ALIGNMENT ((size_t)8U)
+#endif  /* MALLOC_ALIGNMENT */
+#ifndef FOOTERS
+#define FOOTERS 0
+#endif  /* FOOTERS */
+#ifndef ABORT
+#define ABORT  abort()
+#endif  /* ABORT */
+#ifndef ABORT_ON_ASSERT_FAILURE
+#define ABORT_ON_ASSERT_FAILURE 1
+#endif  /* ABORT_ON_ASSERT_FAILURE */
+#ifndef PROCEED_ON_ERROR
+#define PROCEED_ON_ERROR 0
+#endif  /* PROCEED_ON_ERROR */
+#ifndef USE_LOCKS
+#define USE_LOCKS 0
+#endif  /* USE_LOCKS */
+#ifndef USE_SPIN_LOCKS
+#if USE_LOCKS && (defined(__GNUC__) && ((defined(__i386__) || defined(__x86_64__)))) || (defined(_MSC_VER) && _MSC_VER>=1310)
+#define USE_SPIN_LOCKS 1
+#else
+#define USE_SPIN_LOCKS 0
+#endif /* USE_LOCKS && ... */
+#endif /* USE_SPIN_LOCKS */
+#ifndef INSECURE
+#define INSECURE 0
+#endif  /* INSECURE */
+#ifndef HAVE_MMAP
+#define HAVE_MMAP 1
+#endif  /* HAVE_MMAP */
+#ifndef MMAP_CLEARS
+#define MMAP_CLEARS 1
+#endif  /* MMAP_CLEARS */
+#ifndef HAVE_MREMAP
+#ifdef linux
+#define HAVE_MREMAP 1
+#else   /* linux */
+#define HAVE_MREMAP 0
+#endif  /* linux */
+#endif  /* HAVE_MREMAP */
+#ifndef MALLOC_FAILURE_ACTION
+#define MALLOC_FAILURE_ACTION  errno = ENOMEM;
+#endif  /* MALLOC_FAILURE_ACTION */
+#ifndef HAVE_MORECORE
+#if ONLY_MSPACES
+#define HAVE_MORECORE 0
+#else   /* ONLY_MSPACES */
+#define HAVE_MORECORE 1
+#endif  /* ONLY_MSPACES */
+#endif  /* HAVE_MORECORE */
+#if !HAVE_MORECORE
+#define MORECORE_CONTIGUOUS 0
+#else   /* !HAVE_MORECORE */
+#define MORECORE_DEFAULT sbrk
+#ifndef MORECORE_CONTIGUOUS
+#define MORECORE_CONTIGUOUS 1
+#endif  /* MORECORE_CONTIGUOUS */
+#endif  /* HAVE_MORECORE */
+#ifndef DEFAULT_GRANULARITY
+#if (MORECORE_CONTIGUOUS || defined(WIN32))
+#define DEFAULT_GRANULARITY (0)  /* 0 means to compute in init_mparams */
+#else   /* MORECORE_CONTIGUOUS */
+#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U)
+#endif  /* MORECORE_CONTIGUOUS */
+#endif  /* DEFAULT_GRANULARITY */
+#ifndef DEFAULT_TRIM_THRESHOLD
+#ifndef MORECORE_CANNOT_TRIM
+#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U)
+#else   /* MORECORE_CANNOT_TRIM */
+#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T
+#endif  /* MORECORE_CANNOT_TRIM */
+#endif  /* DEFAULT_TRIM_THRESHOLD */
+#ifndef DEFAULT_MMAP_THRESHOLD
+#if HAVE_MMAP
+#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U)
+#else   /* HAVE_MMAP */
+#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
+#endif  /* HAVE_MMAP */
+#endif  /* DEFAULT_MMAP_THRESHOLD */
+#ifndef MAX_RELEASE_CHECK_RATE
+#if HAVE_MMAP
+#define MAX_RELEASE_CHECK_RATE 4095
+#else
+#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T
+#endif /* HAVE_MMAP */
+#endif /* MAX_RELEASE_CHECK_RATE */
+#ifndef USE_BUILTIN_FFS
+#define USE_BUILTIN_FFS 0
+#endif  /* USE_BUILTIN_FFS */
+#ifndef USE_DEV_RANDOM
+#define USE_DEV_RANDOM 0
+#endif  /* USE_DEV_RANDOM */
+#ifndef NO_MALLINFO
+#define NO_MALLINFO 0
+#endif  /* NO_MALLINFO */
+#ifndef MALLINFO_FIELD_TYPE
+#define MALLINFO_FIELD_TYPE size_t
+#endif  /* MALLINFO_FIELD_TYPE */
+#ifndef NO_SEGMENT_TRAVERSAL
+#define NO_SEGMENT_TRAVERSAL 0
+#endif /* NO_SEGMENT_TRAVERSAL */
+
+/*
+  mallopt tuning options.  SVID/XPG defines four standard parameter
+  numbers for mallopt, normally defined in malloc.h.  None of these
+  are used in this malloc, so setting them has no effect. But this
+  malloc does support the following options.
+*/
+
+#define M_TRIM_THRESHOLD     (-1)
+#define M_GRANULARITY        (-2)
+#define M_MMAP_THRESHOLD     (-3)
+
+/* ------------------------ Mallinfo declarations ------------------------ */
+
+#if !NO_MALLINFO
+/*
+  This version of malloc supports the standard SVID/XPG mallinfo
+  routine that returns a struct containing usage properties and
+  statistics. It should work on any system that has a
+  /usr/include/malloc.h defining struct mallinfo.  The main
+  declaration needed is the mallinfo struct that is returned (by-copy)
+  by mallinfo().  The malloinfo struct contains a bunch of fields that
+  are not even meaningful in this version of malloc.  These fields are
+  are instead filled by mallinfo() with other numbers that might be of
+  interest.
+
+  HAVE_USR_INCLUDE_MALLOC_H should be set if you have a
+  /usr/include/malloc.h file that includes a declaration of struct
+  mallinfo.  If so, it is included; else a compliant version is
+  declared below.  These must be precisely the same for mallinfo() to
+  work.  The original SVID version of this struct, defined on most
+  systems with mallinfo, declares all fields as ints. But some others
+  define as unsigned long. If your system defines the fields using a
+  type of different width than listed here, you MUST #include your
+  system version and #define HAVE_USR_INCLUDE_MALLOC_H.
+*/
+
+/* #define HAVE_USR_INCLUDE_MALLOC_H */
+
+#ifdef HAVE_USR_INCLUDE_MALLOC_H
+#include "/usr/include/malloc.h"
+#else /* HAVE_USR_INCLUDE_MALLOC_H */
+#ifndef STRUCT_MALLINFO_DECLARED
+#define STRUCT_MALLINFO_DECLARED 1
+struct mallinfo {
+  MALLINFO_FIELD_TYPE arena;    /* non-mmapped space allocated from system */
+  MALLINFO_FIELD_TYPE ordblks;  /* number of free chunks */
+  MALLINFO_FIELD_TYPE smblks;   /* always 0 */
+  MALLINFO_FIELD_TYPE hblks;    /* always 0 */
+  MALLINFO_FIELD_TYPE hblkhd;   /* space in mmapped regions */
+  MALLINFO_FIELD_TYPE usmblks;  /* maximum total allocated space */
+  MALLINFO_FIELD_TYPE fsmblks;  /* always 0 */
+  MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
+  MALLINFO_FIELD_TYPE fordblks; /* total free space */
+  MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
+};
+#endif /* STRUCT_MALLINFO_DECLARED */
+#endif /* HAVE_USR_INCLUDE_MALLOC_H */
+#endif /* NO_MALLINFO */
+
+/*
+  Try to persuade compilers to inline. The most critical functions for
+  inlining are defined as macros, so these aren't used for them.
+*/
+
+#ifndef FORCEINLINE
+  #if defined(__GNUC__)
+#define FORCEINLINE __inline __attribute__ ((always_inline))
+  #elif defined(_MSC_VER)
+    #define FORCEINLINE __forceinline
+  #endif
+#endif
+#ifndef NOINLINE
+  #if defined(__GNUC__)
+    #define NOINLINE __attribute__ ((noinline))
+  #elif defined(_MSC_VER)
+    #define NOINLINE __declspec(noinline)
+  #else
+    #define NOINLINE
+  #endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#ifndef FORCEINLINE
+ #define FORCEINLINE inline
+#endif
+#endif /* __cplusplus */
+#ifndef FORCEINLINE
+ #define FORCEINLINE
+#endif
+
+#if !ONLY_MSPACES
+
+/* ------------------- Declarations of public routines ------------------- */
+
+#ifndef USE_DL_PREFIX
+#define dlcalloc               calloc
+#define dlfree                 free
+#define dlmalloc               malloc
+#define dlmemalign             memalign
+#define dlrealloc              realloc
+#define dlvalloc               valloc
+#define dlpvalloc              pvalloc
+#define dlmallinfo             mallinfo
+#define dlmallopt              mallopt
+#define dlmalloc_trim          malloc_trim
+#define dlmalloc_stats         malloc_stats
+#define dlmalloc_usable_size   malloc_usable_size
+#define dlmalloc_footprint     malloc_footprint
+#define dlmalloc_max_footprint malloc_max_footprint
+#define dlindependent_calloc   independent_calloc
+#define dlindependent_comalloc independent_comalloc
+#endif /* USE_DL_PREFIX */
+
+
+/*
+  malloc(size_t n)
+  Returns a pointer to a newly allocated chunk of at least n bytes, or
+  null if no space is available, in which case errno is set to ENOMEM
+  on ANSI C systems.
+
+  If n is zero, malloc returns a minimum-sized chunk. (The minimum
+  size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
+  systems.)  Note that size_t is an unsigned type, so calls with
+  arguments that would be negative if signed are interpreted as
+  requests for huge amounts of space, which will often fail. The
+  maximum supported value of n differs across systems, but is in all
+  cases less than the maximum representable value of a size_t.
+*/
+void* dlmalloc(size_t);
+
+/*
+  free(void* p)
+  Releases the chunk of memory pointed to by p, that had been previously
+  allocated using malloc or a related routine such as realloc.
+  It has no effect if p is null. If p was not malloced or already
+  freed, free(p) will by default cause the current program to abort.
+*/
+void  dlfree(void*);
+
+/*
+  calloc(size_t n_elements, size_t element_size);
+  Returns a pointer to n_elements * element_size bytes, with all locations
+  set to zero.
+*/
+void* dlcalloc(size_t, size_t);
+
+/*
+  realloc(void* p, size_t n)
+  Returns a pointer to a chunk of size n that contains the same data
+  as does chunk p up to the minimum of (n, p's size) bytes, or null
+  if no space is available.
+
+  The returned pointer may or may not be the same as p. The algorithm
+  prefers extending p in most cases when possible, otherwise it
+  employs the equivalent of a malloc-copy-free sequence.
+
+  If p is null, realloc is equivalent to malloc.
+
+  If space is not available, realloc returns null, errno is set (if on
+  ANSI) and p is NOT freed.
+
+  if n is for fewer bytes than already held by p, the newly unused
+  space is lopped off and freed if possible.  realloc with a size
+  argument of zero (re)allocates a minimum-sized chunk.
+
+  The old unix realloc convention of allowing the last-free'd chunk
+  to be used as an argument to realloc is not supported.
+*/
+
+void* dlrealloc(void*, size_t);
+
+/*
+  memalign(size_t alignment, size_t n);
+  Returns a pointer to a newly allocated chunk of n bytes, aligned
+  in accord with the alignment argument.
+
+  The alignment argument should be a power of two. If the argument is
+  not a power of two, the nearest greater power is used.
+  8-byte alignment is guaranteed by normal malloc calls, so don't
+  bother calling memalign with an argument of 8 or less.
+
+  Overreliance on memalign is a sure way to fragment space.
+*/
+void* dlmemalign(size_t, size_t);
+
+/*
+  valloc(size_t n);
+  Equivalent to memalign(pagesize, n), where pagesize is the page
+  size of the system. If the pagesize is unknown, 4096 is used.
+*/
+void* dlvalloc(size_t);
+
+/*
+  mallopt(int parameter_number, int parameter_value)
+  Sets tunable parameters The format is to provide a
+  (parameter-number, parameter-value) pair.  mallopt then sets the
+  corresponding parameter to the argument value if it can (i.e., so
+  long as the value is meaningful), and returns 1 if successful else
+  0.  To workaround the fact that mallopt is specified to use int,
+  not size_t parameters, the value -1 is specially treated as the
+  maximum unsigned size_t value.
+
+  SVID/XPG/ANSI defines four standard param numbers for mallopt,
+  normally defined in malloc.h.  None of these are use in this malloc,
+  so setting them has no effect. But this malloc also supports other
+  options in mallopt. See below for details.  Briefly, supported
+  parameters are as follows (listed defaults are for "typical"
+  configurations).
+
+  Symbol            param #  default    allowed param values
+  M_TRIM_THRESHOLD     -1   2*1024*1024   any   (-1 disables)
+  M_GRANULARITY        -2     page size   any power of 2 >= page size
+  M_MMAP_THRESHOLD     -3      256*1024   any   (or 0 if no MMAP support)
+*/
+int dlmallopt(int, int);
+
+/*
+  malloc_footprint();
+  Returns the number of bytes obtained from the system.  The total
+  number of bytes allocated by malloc, realloc etc., is less than this
+  value. Unlike mallinfo, this function returns only a precomputed
+  result, so can be called frequently to monitor memory consumption.
+  Even if locks are otherwise defined, this function does not use them,
+  so results might not be up to date.
+*/
+size_t dlmalloc_footprint(void);
+
+/*
+  malloc_max_footprint();
+  Returns the maximum number of bytes obtained from the system. This
+  value will be greater than current footprint if deallocated space
+  has been reclaimed by the system. The peak number of bytes allocated
+  by malloc, realloc etc., is less than this value. Unlike mallinfo,
+  this function returns only a precomputed result, so can be called
+  frequently to monitor memory consumption.  Even if locks are
+  otherwise defined, this function does not use them, so results might
+  not be up to date.
+*/
+size_t dlmalloc_max_footprint(void);
+
+#if !NO_MALLINFO
+/*
+  mallinfo()
+  Returns (by copy) a struct containing various summary statistics:
+
+  arena:     current total non-mmapped bytes allocated from system
+  ordblks:   the number of free chunks
+  smblks:    always zero.
+  hblks:     current number of mmapped regions
+  hblkhd:    total bytes held in mmapped regions
+  usmblks:   the maximum total allocated space. This will be greater
+               than current total if trimming has occurred.
+  fsmblks:   always zero
+  uordblks:  current total allocated space (normal or mmapped)
+  fordblks:  total free space
+  keepcost:  the maximum number of bytes that could ideally be released
+              back to system via malloc_trim. ("ideally" means that
+              it ignores page restrictions etc.)
+
+  Because these fields are ints, but internal bookkeeping may
+  be kept as longs, the reported values may wrap around zero and
+  thus be inaccurate.
+*/
+struct mallinfo dlmallinfo(void);
+#endif /* NO_MALLINFO */
+
+/*
+  independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
+
+  independent_calloc is similar to calloc, but instead of returning a
+  single cleared space, it returns an array of pointers to n_elements
+  independent elements that can hold contents of size elem_size, each
+  of which starts out cleared, and can be independently freed,
+  realloc'ed etc. The elements are guaranteed to be adjacently
+  allocated (this is not guaranteed to occur with multiple callocs or
+  mallocs), which may also improve cache locality in some
+  applications.
+
+  The "chunks" argument is optional (i.e., may be null, which is
+  probably the most typical usage). If it is null, the returned array
+  is itself dynamically allocated and should also be freed when it is
+  no longer needed. Otherwise, the chunks array must be of at least
+  n_elements in length. It is filled in with the pointers to the
+  chunks.
+
+  In either case, independent_calloc returns this pointer array, or
+  null if the allocation failed.  If n_elements is zero and "chunks"
+  is null, it returns a chunk representing an array with zero elements
+  (which should be freed if not wanted).
+
+  Each element must be individually freed when it is no longer
+  needed. If you'd like to instead be able to free all at once, you
+  should instead use regular calloc and assign pointers into this
+  space to represent elements.  (In this case though, you cannot
+  independently free elements.)
+
+  independent_calloc simplifies and speeds up implementations of many
+  kinds of pools.  It may also be useful when constructing large data
+  structures that initially have a fixed number of fixed-sized nodes,
+  but the number is not known at compile time, and some of the nodes
+  may later need to be freed. For example:
+
+  struct Node { int item; struct Node* next; };
+
+  struct Node* build_list() {
+    struct Node** pool;
+    int n = read_number_of_nodes_needed();
+    if (n <= 0) return 0;
+    pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
+    if (pool == 0) die();
+    // organize into a linked list...
+    struct Node* first = pool[0];
+    for (i = 0; i < n-1; ++i)
+      pool[i]->next = pool[i+1];
+    free(pool);     // Can now free the array (or not, if it is needed later)
+    return first;
+  }
+*/
+void** dlindependent_calloc(size_t, size_t, void**);
+
+/*
+  independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
+
+  independent_comalloc allocates, all at once, a set of n_elements
+  chunks with sizes indicated in the "sizes" array.    It returns
+  an array of pointers to these elements, each of which can be
+  independently freed, realloc'ed etc. The elements are guaranteed to
+  be adjacently allocated (this is not guaranteed to occur with
+  multiple callocs or mallocs), which may also improve cache locality
+  in some applications.
+
+  The "chunks" argument is optional (i.e., may be null). If it is null
+  the returned array is itself dynamically allocated and should also
+  be freed when it is no longer needed. Otherwise, the chunks array
+  must be of at least n_elements in length. It is filled in with the
+  pointers to the chunks.
+
+  In either case, independent_comalloc returns this pointer array, or
+  null if the allocation failed.  If n_elements is zero and chunks is
+  null, it returns a chunk representing an array with zero elements
+  (which should be freed if not wanted).
+
+  Each element must be individually freed when it is no longer
+  needed. If you'd like to instead be able to free all at once, you
+  should instead use a single regular malloc, and assign pointers at
+  particular offsets in the aggregate space. (In this case though, you
+  cannot independently free elements.)
+
+  independent_comallac differs from independent_calloc in that each
+  element may have a different size, and also that it does not
+  automatically clear elements.
+
+  independent_comalloc can be used to speed up allocation in cases
+  where several structs or objects must always be allocated at the
+  same time.  For example:
+
+  struct Head { ... }
+  struct Foot { ... }
+
+  void send_message(char* msg) {
+    int msglen = strlen(msg);
+    size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
+    void* chunks[3];
+    if (independent_comalloc(3, sizes, chunks) == 0)
+      die();
+    struct Head* head = (struct Head*)(chunks[0]);
+    char*        body = (char*)(chunks[1]);
+    struct Foot* foot = (struct Foot*)(chunks[2]);
+    // ...
+  }
+
+  In general though, independent_comalloc is worth using only for
+  larger values of n_elements. For small values, you probably won't
+  detect enough difference from series of malloc calls to bother.
+
+  Overuse of independent_comalloc can increase overall memory usage,
+  since it cannot reuse existing noncontiguous small chunks that
+  might be available for some of the elements.
+*/
+void** dlindependent_comalloc(size_t, size_t*, void**);
+
+
+/*
+  pvalloc(size_t n);
+  Equivalent to valloc(minimum-page-that-holds(n)), that is,
+  round up n to nearest pagesize.
+ */
+void*  dlpvalloc(size_t);
+
+/*
+  malloc_trim(size_t pad);
+
+  If possible, gives memory back to the system (via negative arguments
+  to sbrk) if there is unused memory at the `high' end of the malloc
+  pool or in unused MMAP segments. You can call this after freeing
+  large blocks of memory to potentially reduce the system-level memory
+  requirements of a program. However, it cannot guarantee to reduce
+  memory. Under some allocation patterns, some large free blocks of
+  memory will be locked between two used chunks, so they cannot be
+  given back to the system.
+
+  The `pad' argument to malloc_trim represents the amount of free
+  trailing space to leave untrimmed. If this argument is zero, only
+  the minimum amount of memory to maintain internal data structures
+  will be left. Non-zero arguments can be supplied to maintain enough
+  trailing space to service future expected allocations without having
+  to re-obtain memory from the system.
+
+  Malloc_trim returns 1 if it actually released any memory, else 0.
+*/
+int  dlmalloc_trim(size_t);
+
+/*
+  malloc_stats();
+  Prints on stderr the amount of space obtained from the system (both
+  via sbrk and mmap), the maximum amount (which may be more than
+  current if malloc_trim and/or munmap got called), and the current
+  number of bytes allocated via malloc (or realloc, etc) but not yet
+  freed. Note that this is the number of bytes allocated, not the
+  number requested. It will be larger than the number requested
+  because of alignment and bookkeeping overhead. Because it includes
+  alignment wastage as being in use, this figure may be greater than
+  zero even when no user-level chunks are allocated.
+
+  The reported current and maximum system memory can be inaccurate if
+  a program makes other calls to system memory allocation functions
+  (normally sbrk) outside of malloc.
+
+  malloc_stats prints only the most commonly interesting statistics.
+  More information can be obtained by calling mallinfo.
+*/
+void  dlmalloc_stats(void);
+
+#endif /* ONLY_MSPACES */
+
+/*
+  malloc_usable_size(void* p);
+
+  Returns the number of bytes you can actually use in
+  an allocated chunk, which may be more than you requested (although
+  often not) due to alignment and minimum size constraints.
+  You can use this many bytes without worrying about
+  overwriting other allocated objects. This is not a particularly great
+  programming practice. malloc_usable_size can be more useful in
+  debugging and assertions, for example:
+
+  p = malloc(n);
+  assert(malloc_usable_size(p) >= 256);
+*/
+size_t dlmalloc_usable_size(void*);
+
+
+#if MSPACES
+
+/*
+  mspace is an opaque type representing an independent
+  region of space that supports mspace_malloc, etc.
+*/
+typedef void* mspace;
+
+/*
+  create_mspace creates and returns a new independent space with the
+  given initial capacity, or, if 0, the default granularity size.  It
+  returns null if there is no system memory available to create the
+  space.  If argument locked is non-zero, the space uses a separate
+  lock to control access. The capacity of the space will grow
+  dynamically as needed to service mspace_malloc requests.  You can
+  control the sizes of incremental increases of this space by
+  compiling with a different DEFAULT_GRANULARITY or dynamically
+  setting with mallopt(M_GRANULARITY, value).
+*/
+mspace create_mspace(size_t capacity, int locked);
+
+/*
+  destroy_mspace destroys the given space, and attempts to return all
+  of its memory back to the system, returning the total number of
+  bytes freed. After destruction, the results of access to all memory
+  used by the space become undefined.
+*/
+size_t destroy_mspace(mspace msp);
+
+/*
+  create_mspace_with_base uses the memory supplied as the initial base
+  of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
+  space is used for bookkeeping, so the capacity must be at least this
+  large. (Otherwise 0 is returned.) When this initial space is
+  exhausted, additional memory will be obtained from the system.
+  Destroying this space will deallocate all additionally allocated
+  space (if possible) but not the initial base.
+*/
+mspace create_mspace_with_base(void* base, size_t capacity, int locked);
+
+/*
+  mspace_mmap_large_chunks controls whether requests for large chunks
+  are allocated in their own mmapped regions, separate from others in
+  this mspace. By default this is enabled, which reduces
+  fragmentation. However, such chunks are not necessarily released to
+  the system upon destroy_mspace.  Disabling by setting to false may
+  increase fragmentation, but avoids leakage when relying on
+  destroy_mspace to release all memory allocated using this space.
+*/
+int mspace_mmap_large_chunks(mspace msp, int enable);
+
+
+/*
+  mspace_malloc behaves as malloc, but operates within
+  the given space.
+*/
+void* mspace_malloc(mspace msp, size_t bytes);
+
+/*
+  mspace_free behaves as free, but operates within
+  the given space.
+
+  If compiled with FOOTERS==1, mspace_free is not actually needed.
+  free may be called instead of mspace_free because freed chunks from
+  any space are handled by their originating spaces.
+*/
+void mspace_free(mspace msp, void* mem);
+
+/*
+  mspace_realloc behaves as realloc, but operates within
+  the given space.
+
+  If compiled with FOOTERS==1, mspace_realloc is not actually
+  needed.  realloc may be called instead of mspace_realloc because
+  realloced chunks from any space are handled by their originating
+  spaces.
+*/
+void* mspace_realloc(mspace msp, void* mem, size_t newsize);
+
+/*
+  mspace_calloc behaves as calloc, but operates within
+  the given space.
+*/
+void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
+
+/*
+  mspace_memalign behaves as memalign, but operates within
+  the given space.
+*/
+void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
+
+/*
+  mspace_independent_calloc behaves as independent_calloc, but
+  operates within the given space.
+*/
+void** mspace_independent_calloc(mspace msp, size_t n_elements,
+                                size_t elem_size, void* chunks[]);
+
+/*
+  mspace_independent_comalloc behaves as independent_comalloc, but
+  operates within the given space.
+*/
+void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+                                  size_t sizes[], void* chunks[]);
+
+/*
+  mspace_footprint() returns the number of bytes obtained from the
+  system for this space.
+*/
+size_t mspace_footprint(mspace msp);
+
+/*
+  mspace_max_footprint() returns the peak number of bytes obtained from the
+  system for this space.
+*/
+size_t mspace_max_footprint(mspace msp);
+
+
+#if !NO_MALLINFO
+/*
+  mspace_mallinfo behaves as mallinfo, but reports properties of
+  the given space.
+*/
+struct mallinfo mspace_mallinfo(mspace msp);
+#endif /* NO_MALLINFO */
+
+/*
+  malloc_usable_size(void* p) behaves the same as malloc_usable_size;
+*/
+  size_t mspace_usable_size(void* mem);
+
+/*
+  mspace_malloc_stats behaves as malloc_stats, but reports
+  properties of the given space.
+*/
+void mspace_malloc_stats(mspace msp);
+
+/*
+  mspace_trim behaves as malloc_trim, but
+  operates within the given space.
+*/
+int mspace_trim(mspace msp, size_t pad);
+
+/*
+  An alias for mallopt.
+*/
+int mspace_mallopt(int, int);
+
+#endif /* MSPACES */
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif /* __cplusplus */
+
+/*
+  ========================================================================
+  To make a fully customizable malloc.h header file, cut everything
+  above this line, put into file malloc.h, edit to suit, and #include it
+  on the next line, as well as in programs that use this malloc.
+  ========================================================================
+*/
+
+/* #include "malloc.h" */
+
+/*------------------------------ internal #includes ---------------------- */
+
+#ifdef WIN32
+#ifndef __GNUC__
+#pragma warning( disable : 4146 ) /* no "unsigned" warnings */
+#endif
+#endif /* WIN32 */
+
+#include <stdio.h>       /* for printing in malloc_stats */
+
+#ifndef LACKS_ERRNO_H
+#include <errno.h>       /* for MALLOC_FAILURE_ACTION */
+#endif /* LACKS_ERRNO_H */
+#if FOOTERS
+#include <time.h>        /* for magic initialization */
+#endif /* FOOTERS */
+#ifndef LACKS_STDLIB_H
+#include <stdlib.h>      /* for abort() */
+#endif /* LACKS_STDLIB_H */
+#ifdef DEBUG
+#if ABORT_ON_ASSERT_FAILURE
+#define assert(x) if(!(x)) ABORT
+#else /* ABORT_ON_ASSERT_FAILURE */
+#include <assert.h>
+#endif /* ABORT_ON_ASSERT_FAILURE */
+#else  /* DEBUG */
+#ifndef assert
+#define assert(x)
+#endif
+#define DEBUG 0
+#endif /* DEBUG */
+#ifndef LACKS_STRING_H
+#include <string.h>      /* for memset etc */
+#endif  /* LACKS_STRING_H */
+#if USE_BUILTIN_FFS
+#ifndef LACKS_STRINGS_H
+#include <strings.h>     /* for ffs */
+#endif /* LACKS_STRINGS_H */
+#endif /* USE_BUILTIN_FFS */
+#if HAVE_MMAP
+#ifndef LACKS_SYS_MMAN_H
+#include <sys/mman.h>    /* for mmap */
+#endif /* LACKS_SYS_MMAN_H */
+#ifndef LACKS_FCNTL_H
+#include <fcntl.h>
+#endif /* LACKS_FCNTL_H */
+#endif /* HAVE_MMAP */
+#ifndef LACKS_UNISTD_H
+#include <unistd.h>     /* for sbrk, sysconf */
+#else /* LACKS_UNISTD_H */
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+extern void*     sbrk(ptrdiff_t);
+#endif /* FreeBSD etc */
+#endif /* LACKS_UNISTD_H */
+
+/* Declarations for locking */
+#if USE_LOCKS
+#ifndef WIN32
+#include <pthread.h>
+#if defined (__SVR4) && defined (__sun)  /* solaris */
+#include <thread.h>
+#endif /* solaris */
+#else
+#ifndef _M_AMD64
+/* These are already defined on AMD64 builds */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#ifndef __MINGW32__
+LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp);
+LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value);
+#endif
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _M_AMD64 */
+#ifndef __MINGW32__
+#pragma intrinsic (_InterlockedCompareExchange)
+#pragma intrinsic (_InterlockedExchange)
+#else
+  /* --[ start GCC compatibility ]----------------------------------------------
+   * Compatibility <intrin_x86.h> header for GCC -- GCC equivalents of intrinsic
+   * Microsoft Visual C++ functions. Originally developed for the ReactOS
+   * (<http://www.reactos.org/>) and TinyKrnl (<http://www.tinykrnl.org/>)
+   * projects.
+   *
+   * Copyright (c) 2006 KJK::Hyperion <hackbunny@reactos.com>
+   *
+   * 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.
+   */
+
+  /*** Atomic operations ***/
+  #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100
+    #define _ReadWriteBarrier() __sync_synchronize()
+  #else
+    static __inline__ __attribute__((always_inline)) long __sync_lock_test_and_set(volatile long * const Target, const long Value)
+    {
+      long res;
+      __asm__ __volatile__("xchg%z0 %2, %0" : "=g" (*(Target)), "=r" (res) : "1" (Value));
+      return res;
+    }
+    static void __inline__ __attribute__((always_inline)) _MemoryBarrier(void)
+    {
+      __asm__ __volatile__("" : : : "memory");
+    }
+    #define _ReadWriteBarrier() _MemoryBarrier()
+  #endif
+  /* BUGBUG: GCC only supports full barriers */
+  static __inline__ __attribute__((always_inline)) long _InterlockedExchange(volatile long * const Target, const long Value)
+  {
+    /* NOTE: __sync_lock_test_and_set would be an acquire barrier, so we force a full barrier */
+    _ReadWriteBarrier();
+    return __sync_lock_test_and_set(Target, Value);
+  }
+  /* --[ end GCC compatibility ]---------------------------------------------- */
+#endif
+#define interlockedcompareexchange _InterlockedCompareExchange
+#define interlockedexchange _InterlockedExchange
+#endif /* Win32 */
+#endif /* USE_LOCKS */
+
+/* Declarations for bit scanning on win32 */
+#if defined(_MSC_VER) && _MSC_VER>=1300
+#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+unsigned char _BitScanForward(unsigned long *index, unsigned long mask);
+unsigned char _BitScanReverse(unsigned long *index, unsigned long mask);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#define BitScanForward _BitScanForward
+#define BitScanReverse _BitScanReverse
+#pragma intrinsic(_BitScanForward)
+#pragma intrinsic(_BitScanReverse)
+#endif /* BitScanForward */
+#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */
+
+#ifndef WIN32
+#ifndef malloc_getpagesize
+#  ifdef _SC_PAGESIZE         /* some SVR4 systems omit an underscore */
+#    ifndef _SC_PAGE_SIZE
+#      define _SC_PAGE_SIZE _SC_PAGESIZE
+#    endif
+#  endif
+#  ifdef _SC_PAGE_SIZE
+#    define malloc_getpagesize sysconf(_SC_PAGE_SIZE)
+#  else
+#    if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE)
+       extern size_t getpagesize();
+#      define malloc_getpagesize getpagesize()
+#    else
+#      ifdef WIN32 /* use supplied emulation of getpagesize */
+#        define malloc_getpagesize getpagesize()
+#      else
+#        ifndef LACKS_SYS_PARAM_H
+#          include <sys/param.h>
+#        endif
+#        ifdef EXEC_PAGESIZE
+#          define malloc_getpagesize EXEC_PAGESIZE
+#        else
+#          ifdef NBPG
+#            ifndef CLSIZE
+#              define malloc_getpagesize NBPG
+#            else
+#              define malloc_getpagesize (NBPG * CLSIZE)
+#            endif
+#          else
+#            ifdef NBPC
+#              define malloc_getpagesize NBPC
+#            else
+#              ifdef PAGESIZE
+#                define malloc_getpagesize PAGESIZE
+#              else /* just guess */
+#                define malloc_getpagesize ((size_t)4096U)
+#              endif
+#            endif
+#          endif
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+#endif
+
+
+
+/* ------------------- size_t and alignment properties -------------------- */
+
+/* The byte and bit size of a size_t */
+#define SIZE_T_SIZE         (sizeof(size_t))
+#define SIZE_T_BITSIZE      (sizeof(size_t) << 3)
+
+/* Some constants coerced to size_t */
+/* Annoying but necessary to avoid errors on some platforms */
+#define SIZE_T_ZERO         ((size_t)0)
+#define SIZE_T_ONE          ((size_t)1)
+#define SIZE_T_TWO          ((size_t)2)
+#define SIZE_T_FOUR         ((size_t)4)
+#define TWO_SIZE_T_SIZES    (SIZE_T_SIZE<<1)
+#define FOUR_SIZE_T_SIZES   (SIZE_T_SIZE<<2)
+#define SIX_SIZE_T_SIZES    (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES)
+#define HALF_MAX_SIZE_T     (MAX_SIZE_T / 2U)
+
+/* The bit mask value corresponding to MALLOC_ALIGNMENT */
+#define CHUNK_ALIGN_MASK    (MALLOC_ALIGNMENT - SIZE_T_ONE)
+
+/* True if address a has acceptable alignment */
+#define is_aligned(A)       (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0)
+
+/* the number of bytes to offset an address to align it */
+#define align_offset(A)\
+ ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\
+  ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK))
+
+/* -------------------------- MMAP preliminaries ------------------------- */
+
+/*
+   If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and
+   checks to fail so compiler optimizer can delete code rather than
+   using so many "#if"s.
+*/
+
+
+/* MORECORE and MMAP must return MFAIL on failure */
+#define MFAIL                ((void*)(MAX_SIZE_T))
+#define CMFAIL               ((char*)(MFAIL)) /* defined for convenience */
+
+#if HAVE_MMAP
+
+#ifndef WIN32
+#define MUNMAP_DEFAULT(a, s)  munmap((a), (s))
+#define MMAP_PROT            (PROT_READ|PROT_WRITE)
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#define MAP_ANONYMOUS        MAP_ANON
+#endif /* MAP_ANON */
+#ifdef MAP_ANONYMOUS
+#define MMAP_FLAGS           (MAP_PRIVATE|MAP_ANONYMOUS)
+#define MMAP_DEFAULT(s)       mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
+#else /* MAP_ANONYMOUS */
+/*
+   Nearly all versions of mmap support MAP_ANONYMOUS, so the following
+   is unlikely to be needed, but is supplied just in case.
+*/
+#define MMAP_FLAGS           (MAP_PRIVATE)
+static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
+#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \
+          (dev_zero_fd = open("/dev/zero", O_RDWR), \
+           mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \
+           mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0))
+#endif /* MAP_ANONYMOUS */
+
+#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s)
+
+#else /* WIN32 */
+
+/* Win32 MMAP via VirtualAlloc */
+static FORCEINLINE void* win32mmap(size_t size) {
+  void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
+  return (ptr != 0)? ptr: MFAIL;
+}
+
+/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
+static FORCEINLINE void* win32direct_mmap(size_t size) {
+  void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
+                          PAGE_READWRITE);
+  return (ptr != 0)? ptr: MFAIL;
+}
+
+/* This function supports releasing coalesed segments */
+static FORCEINLINE int win32munmap(void* ptr, size_t size) {
+  MEMORY_BASIC_INFORMATION minfo;
+  char* cptr = (char*)ptr;
+  while (size) {
+    if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0)
+      return -1;
+    if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr ||
+       minfo.State != MEM_COMMIT || minfo.RegionSize > size)
+      return -1;
+    if (VirtualFree(cptr, 0, MEM_RELEASE) == 0)
+      return -1;
+    cptr += minfo.RegionSize;
+    size -= minfo.RegionSize;
+  }
+  return 0;
+}
+
+#define MMAP_DEFAULT(s)             win32mmap(s)
+#define MUNMAP_DEFAULT(a, s)        win32munmap((a), (s))
+#define DIRECT_MMAP_DEFAULT(s)      win32direct_mmap(s)
+#endif /* WIN32 */
+#endif /* HAVE_MMAP */
+
+#if HAVE_MREMAP
+#ifndef WIN32
+#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv))
+#endif /* WIN32 */
+#endif /* HAVE_MREMAP */
+
+
+/**
+ * Define CALL_MORECORE
+ */
+#if HAVE_MORECORE
+    #ifdef MORECORE
+       #define CALL_MORECORE(S)    MORECORE(S)
+    #else  /* MORECORE */
+       #define CALL_MORECORE(S)    MORECORE_DEFAULT(S)
+    #endif /* MORECORE */
+#else  /* HAVE_MORECORE */
+    #define CALL_MORECORE(S)        MFAIL
+#endif /* HAVE_MORECORE */
+
+/**
+ * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP
+ */
+#if HAVE_MMAP
+    #define IS_MMAPPED_BIT          (SIZE_T_ONE)
+    #define USE_MMAP_BIT            (SIZE_T_ONE)
+
+    #ifdef MMAP
+       #define CALL_MMAP(s)        MMAP(s)
+    #else /* MMAP */
+       #define CALL_MMAP(s)        MMAP_DEFAULT(s)
+    #endif /* MMAP */
+    #ifdef MUNMAP
+       #define CALL_MUNMAP(a, s)   MUNMAP((a), (s))
+    #else /* MUNMAP */
+       #define CALL_MUNMAP(a, s)   MUNMAP_DEFAULT((a), (s))
+    #endif /* MUNMAP */
+    #ifdef DIRECT_MMAP
+       #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s)
+    #else /* DIRECT_MMAP */
+       #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s)
+    #endif /* DIRECT_MMAP */
+#else  /* HAVE_MMAP */
+    #define IS_MMAPPED_BIT          (SIZE_T_ZERO)
+    #define USE_MMAP_BIT            (SIZE_T_ZERO)
+
+    #define MMAP(s)                 MFAIL
+    #define MUNMAP(a, s)            (-1)
+    #define DIRECT_MMAP(s)          MFAIL
+    #define CALL_DIRECT_MMAP(s)     DIRECT_MMAP(s)
+    #define CALL_MMAP(s)            MMAP(s)
+    #define CALL_MUNMAP(a, s)       MUNMAP((a), (s))
+#endif /* HAVE_MMAP */
+
+/**
+ * Define CALL_MREMAP
+ */
+#if HAVE_MMAP && HAVE_MREMAP
+    #ifdef MREMAP
+       #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv))
+    #else /* MREMAP */
+       #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv))
+    #endif /* MREMAP */
+#else  /* HAVE_MMAP && HAVE_MREMAP */
+    #define CALL_MREMAP(addr, osz, nsz, mv)     MFAIL
+#endif /* HAVE_MMAP && HAVE_MREMAP */
+
+/* mstate bit set if continguous morecore disabled or failed */
+#define USE_NONCONTIGUOUS_BIT (4U)
+
+/* segment bit set in create_mspace_with_base */
+#define EXTERN_BIT            (8U)
+
+
+/* --------------------------- Lock preliminaries ------------------------ */
+
+/*
+  When locks are defined, there is one global lock, plus
+  one per-mspace lock.
+
+  The global lock_ensures that mparams.magic and other unique
+  mparams values are initialized only once. It also protects
+  sequences of calls to MORECORE.  In many cases sys_alloc requires
+  two calls, that should not be interleaved with calls by other
+  threads.  This does not protect against direct calls to MORECORE
+  by other threads not using this lock, so there is still code to
+  cope the best we can on interference.
+
+  Per-mspace locks surround calls to malloc, free, etc.  To enable use
+  in layered extensions, per-mspace locks are reentrant.
+
+  Because lock-protected regions generally have bounded times, it is
+  OK to use the supplied simple spinlocks in the custom versions for
+  x86.
+
+  If USE_LOCKS is > 1, the definitions of lock routines here are
+  bypassed, in which case you will need to define at least
+  INITIAL_LOCK, ACQUIRE_LOCK, RELEASE_LOCK and possibly TRY_LOCK
+  (which is not used in this malloc, but commonly needed in
+  extensions.)
+*/
+
+#if USE_LOCKS == 1
+
+#if USE_SPIN_LOCKS
+#ifndef WIN32
+
+/* Custom pthread-style spin locks on x86 and x64 for gcc */
+struct pthread_mlock_t {
+  volatile unsigned int l;
+  volatile unsigned int c;
+  volatile pthread_t threadid;
+};
+#define MLOCK_T struct        pthread_mlock_t
+#define CURRENT_THREAD        pthread_self()
+#define INITIAL_LOCK(sl)      (memset(sl, 0, sizeof(MLOCK_T)), 0)
+#define ACQUIRE_LOCK(sl)      pthread_acquire_lock(sl)
+#define RELEASE_LOCK(sl)      pthread_release_lock(sl)
+#define TRY_LOCK(sl)          pthread_try_lock(sl)
+#define SPINS_PER_YIELD       63
+
+static MLOCK_T malloc_global_mutex = { 0, 0, 0};
+
+static FORCEINLINE int pthread_acquire_lock (MLOCK_T *sl) {
+  int spins = 0;
+  volatile unsigned int* lp = &sl->l;
+  for (;;) {
+    if (*lp != 0) {
+      if (sl->threadid == CURRENT_THREAD) {
+       ++sl->c;
+       return 0;
+      }
+    }
+    else {
+      /* place args to cmpxchgl in locals to evade oddities in some gccs */
+      int cmp = 0;
+      int val = 1;
+      int ret;
+      __asm__ __volatile__  ("lock; cmpxchgl %1, %2"
+                            : "=a" (ret)
+                            : "r" (val), "m" (*(lp)), "0"(cmp)
+                            : "memory", "cc");
+      if (!ret) {
+       assert(!sl->threadid);
+       sl->c = 1;
+       sl->threadid = CURRENT_THREAD;
+       return 0;
+      }
+      if ((++spins & SPINS_PER_YIELD) == 0) {
+#if defined (__SVR4) && defined (__sun) /* solaris */
+       thr_yield();
+#else
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
+       sched_yield();
+#else  /* no-op yield on unknown systems */
+       ;
+#endif /* __linux__ || __FreeBSD__ || __APPLE__ */
+#endif /* solaris */
+      }
+    }
+  }
+}
+
+static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) {
+  assert(sl->l != 0);
+  assert(sl->threadid == CURRENT_THREAD);
+  if (--sl->c == 0) {
+    sl->threadid = 0;
+    volatile unsigned int* lp = &sl->l;
+    int prev = 0;
+    int ret;
+    __asm__ __volatile__ ("lock; xchgl %0, %1"
+                         : "=r" (ret)
+                         : "m" (*(lp)), "0"(prev)
+                         : "memory");
+  }
+}
+
+static FORCEINLINE int pthread_try_lock (MLOCK_T *sl) {
+  volatile unsigned int* lp = &sl->l;
+  if (*lp != 0) {
+      if (sl->threadid == CURRENT_THREAD) {
+       ++sl->c;
+       return 1;
+      }
+  }
+  else {
+    int cmp = 0;
+    int val = 1;
+    int ret;
+    __asm__ __volatile__  ("lock; cmpxchgl %1, %2"
+                          : "=a" (ret)
+                          : "r" (val), "m" (*(lp)), "0"(cmp)
+                          : "memory", "cc");
+    if (!ret) {
+      assert(!sl->threadid);
+      sl->c = 1;
+      sl->threadid = CURRENT_THREAD;
+      return 1;
+    }
+  }
+  return 0;
+}
+
+
+#else /* WIN32 */
+/* Custom win32-style spin locks on x86 and x64 for MSC */
+struct win32_mlock_t
+{
+  volatile long l;
+  volatile unsigned int c;
+  volatile long threadid;
+};
+
+#define MLOCK_T               struct win32_mlock_t
+#define CURRENT_THREAD        win32_getcurrentthreadid()
+#define INITIAL_LOCK(sl)      (memset(sl, 0, sizeof(MLOCK_T)), 0)
+#define ACQUIRE_LOCK(sl)      win32_acquire_lock(sl)
+#define RELEASE_LOCK(sl)      win32_release_lock(sl)
+#define TRY_LOCK(sl)          win32_try_lock(sl)
+#define SPINS_PER_YIELD       63
+
+static MLOCK_T malloc_global_mutex = { 0, 0, 0};
+
+static FORCEINLINE long win32_getcurrentthreadid() {
+#ifdef _MSC_VER
+#if defined(_M_IX86)
+  long *threadstruct=(long *)__readfsdword(0x18);
+  long threadid=threadstruct[0x24/sizeof(long)];
+  return threadid;
+#elif defined(_M_X64)
+  /* todo */
+  return GetCurrentThreadId();
+#else
+  return GetCurrentThreadId();
+#endif
+#else
+  return GetCurrentThreadId();
+#endif
+}
+
+static FORCEINLINE int win32_acquire_lock (MLOCK_T *sl) {
+  int spins = 0;
+  for (;;) {
+    if (sl->l != 0) {
+      if (sl->threadid == CURRENT_THREAD) {
+       ++sl->c;
+       return 0;
+      }
+    }
+    else {
+      if (!interlockedexchange(&sl->l, 1)) {
+       assert(!sl->threadid);
+               sl->c=CURRENT_THREAD;
+       sl->threadid = CURRENT_THREAD;
+       sl->c = 1;
+       return 0;
+      }
+    }
+    if ((++spins & SPINS_PER_YIELD) == 0)
+      SleepEx(0, FALSE);
+  }
+}
+
+static FORCEINLINE void win32_release_lock (MLOCK_T *sl) {
+  assert(sl->threadid == CURRENT_THREAD);
+  assert(sl->l != 0);
+  if (--sl->c == 0) {
+    sl->threadid = 0;
+    interlockedexchange (&sl->l, 0);
+  }
+}
+
+static FORCEINLINE int win32_try_lock (MLOCK_T *sl) {
+  if(sl->l != 0) {
+      if (sl->threadid == CURRENT_THREAD) {
+       ++sl->c;
+       return 1;
+      }
+  }
+  else {
+    if (!interlockedexchange(&sl->l, 1)){
+      assert(!sl->threadid);
+      sl->threadid = CURRENT_THREAD;
+      sl->c = 1;
+      return 1;
+    }
+  }
+  return 0;
+}
+
+#endif /* WIN32 */
+#else /* USE_SPIN_LOCKS */
+
+#ifndef WIN32
+/* pthreads-based locks */
+
+#define MLOCK_T               pthread_mutex_t
+#define CURRENT_THREAD        pthread_self()
+#define INITIAL_LOCK(sl)      pthread_init_lock(sl)
+#define ACQUIRE_LOCK(sl)      pthread_mutex_lock(sl)
+#define RELEASE_LOCK(sl)      pthread_mutex_unlock(sl)
+#define TRY_LOCK(sl)          (!pthread_mutex_trylock(sl))
+
+static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Cope with old-style linux recursive lock initialization by adding */
+/* skipped internal declaration from pthread.h */
+#ifdef linux
+#ifndef PTHREAD_MUTEX_RECURSIVE
+extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr,
+                                          int __kind));
+#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
+#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y)
+#endif
+#endif
+
+static int pthread_init_lock (MLOCK_T *sl) {
+  pthread_mutexattr_t attr;
+  if (pthread_mutexattr_init(&attr)) return 1;
+  if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1;
+  if (pthread_mutex_init(sl, &attr)) return 1;
+  if (pthread_mutexattr_destroy(&attr)) return 1;
+  return 0;
+}
+
+#else /* WIN32 */
+/* Win32 critical sections */
+#define MLOCK_T               CRITICAL_SECTION
+#define CURRENT_THREAD        GetCurrentThreadId()
+#define INITIAL_LOCK(s)       (!InitializeCriticalSectionAndSpinCount((s), 0x80000000|4000))
+#define ACQUIRE_LOCK(s)       (EnterCriticalSection(s), 0)
+#define RELEASE_LOCK(s)       LeaveCriticalSection(s)
+#define TRY_LOCK(s)           TryEnterCriticalSection(s)
+#define NEED_GLOBAL_LOCK_INIT
+
+static MLOCK_T malloc_global_mutex;
+static volatile long malloc_global_mutex_status;
+
+/* Use spin loop to initialize global lock */
+static void init_malloc_global_mutex() {
+  for (;;) {
+    long stat = malloc_global_mutex_status;
+    if (stat > 0)
+      return;
+    /* transition to < 0 while initializing, then to > 0) */
+    if (stat == 0 &&
+       interlockedcompareexchange(&malloc_global_mutex_status, -1, 0) == 0) {
+      InitializeCriticalSection(&malloc_global_mutex);
+      interlockedexchange(&malloc_global_mutex_status,1);
+      return;
+    }
+    SleepEx(0, FALSE);
+  }
+}
+
+#endif /* WIN32 */
+#endif /* USE_SPIN_LOCKS */
+#endif /* USE_LOCKS == 1 */
+
+/* -----------------------  User-defined locks ------------------------ */
+
+#if USE_LOCKS > 1
+/* Define your own lock implementation here */
+/* #define INITIAL_LOCK(sl)  ... */
+/* #define ACQUIRE_LOCK(sl)  ... */
+/* #define RELEASE_LOCK(sl)  ... */
+/* #define TRY_LOCK(sl) ... */
+/* static MLOCK_T malloc_global_mutex = ... */
+#endif /* USE_LOCKS > 1 */
+
+/* -----------------------  Lock-based state ------------------------ */
+
+#if USE_LOCKS
+#define USE_LOCK_BIT               (2U)
+#else  /* USE_LOCKS */
+#define USE_LOCK_BIT               (0U)
+#define INITIAL_LOCK(l)
+#endif /* USE_LOCKS */
+
+#if USE_LOCKS
+#define ACQUIRE_MALLOC_GLOBAL_LOCK()  ACQUIRE_LOCK(&malloc_global_mutex);
+#define RELEASE_MALLOC_GLOBAL_LOCK()  RELEASE_LOCK(&malloc_global_mutex);
+#else  /* USE_LOCKS */
+#define ACQUIRE_MALLOC_GLOBAL_LOCK()
+#define RELEASE_MALLOC_GLOBAL_LOCK()
+#endif /* USE_LOCKS */
+
+
+/* -----------------------  Chunk representations ------------------------ */
+
+/*
+  (The following includes lightly edited explanations by Colin Plumb.)
+
+  The malloc_chunk declaration below is misleading (but accurate and
+  necessary).  It declares a "view" into memory allowing access to
+  necessary fields at known offsets from a given base.
+
+  Chunks of memory are maintained using a `boundary tag' method as
+  originally described by Knuth.  (See the paper by Paul Wilson
+  ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such
+  techniques.)  Sizes of free chunks are stored both in the front of
+  each chunk and at the end.  This makes consolidating fragmented
+  chunks into bigger chunks fast.  The head fields also hold bits
+  representing whether chunks are free or in use.
+
+  Here are some pictures to make it clearer.  They are "exploded" to
+  show that the state of a chunk can be thought of as extending from
+  the high 31 bits of the head field of its header through the
+  prev_foot and PINUSE_BIT bit of the following chunk header.
+
+  A chunk that's in use looks like:
+
+   chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+          | Size of previous chunk (if P = 0)                             |
+          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
+        | Size of this chunk                                         1| +-+
+   mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        |                                                               |
+        +-                                                             -+
+        |                                                               |
+        +-                                                             -+
+        |                                                               :
+        +-      size - sizeof(size_t) available payload bytes          -+
+        :                                                               |
+ chunk-> +-                                                             -+
+        |                                                               |
+        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|
+       | Size of next chunk (may or may not be in use)               | +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    And if it's free, it looks like this:
+
+   chunk-> +-                                                             -+
+          | User payload (must be in use, or we would have merged!)       |
+          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
+        | Size of this chunk                                         0| +-+
+   mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        | Next pointer                                                  |
+        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        | Prev pointer                                                  |
+        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        |                                                               :
+        +-      size - sizeof(struct chunk) unused bytes               -+
+        :                                                               |
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        | Size of this chunk                                            |
+        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|
+       | Size of next chunk (must be in use, or we would have merged)| +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                                                               :
+       +- User payload                                                -+
+       :                                                               |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                                                                    |0|
+                                                                    +-+
+  Note that since we always merge adjacent free chunks, the chunks
+  adjacent to a free chunk must be in use.
+
+  Given a pointer to a chunk (which can be derived trivially from the
+  payload pointer) we can, in O(1) time, find out whether the adjacent
+  chunks are free, and if so, unlink them from the lists that they
+  are on and merge them with the current chunk.
+
+  Chunks always begin on even word boundaries, so the mem portion
+  (which is returned to the user) is also on an even word boundary, and
+  thus at least double-word aligned.
+
+  The P (PINUSE_BIT) bit, stored in the unused low-order bit of the
+  chunk size (which is always a multiple of two words), is an in-use
+  bit for the *previous* chunk.  If that bit is *clear*, then the
+  word before the current chunk size contains the previous chunk
+  size, and can be used to find the front of the previous chunk.
+  The very first chunk allocated always has this bit set, preventing
+  access to non-existent (or non-owned) memory. If pinuse is set for
+  any given chunk, then you CANNOT determine the size of the
+  previous chunk, and might even get a memory addressing fault when
+  trying to do so.
+
+  The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of
+  the chunk size redundantly records whether the current chunk is
+  inuse. This redundancy enables usage checks within free and realloc,
+  and reduces indirection when freeing and consolidating chunks.
+
+  Each freshly allocated chunk must have both cinuse and pinuse set.
+  That is, each allocated chunk borders either a previously allocated
+  and still in-use chunk, or the base of its memory arena. This is
+  ensured by making all allocations from the the `lowest' part of any
+  found chunk.  Further, no free chunk physically borders another one,
+  so each free chunk is known to be preceded and followed by either
+  inuse chunks or the ends of memory.
+
+  Note that the `foot' of the current chunk is actually represented
+  as the prev_foot of the NEXT chunk. This makes it easier to
+  deal with alignments etc but can be very confusing when trying
+  to extend or adapt this code.
+
+  The exceptions to all this are
+
+     1. The special chunk `top' is the top-most available chunk (i.e.,
+       the one bordering the end of available memory). It is treated
+       specially.  Top is never included in any bin, is used only if
+       no other chunk is available, and is released back to the
+       system if it is very large (see M_TRIM_THRESHOLD).  In effect,
+       the top chunk is treated as larger (and thus less well
+       fitting) than any other available chunk.  The top chunk
+       doesn't update its trailing size field since there is no next
+       contiguous chunk that would have to index off it. However,
+       space is still allocated for it (TOP_FOOT_SIZE) to enable
+       separation or merging when space is extended.
+
+     3. Chunks allocated via mmap, which have the lowest-order bit
+       (IS_MMAPPED_BIT) set in their prev_foot fields, and do not set
+       PINUSE_BIT in their head fields.  Because they are allocated
+       one-by-one, each must carry its own prev_foot field, which is
+       also used to hold the offset this chunk has within its mmapped
+       region, which is needed to preserve alignment. Each mmapped
+       chunk is trailed by the first two fields of a fake next-chunk
+       for sake of usage checks.
+
+*/
+
+struct malloc_chunk {
+  size_t               prev_foot;  /* Size of previous chunk (if free).  */
+  size_t               head;       /* Size and inuse bits. */
+  struct malloc_chunk* fd;         /* double links -- used only if free. */
+  struct malloc_chunk* bk;
+};
+
+typedef struct malloc_chunk  mchunk;
+typedef struct malloc_chunk* mchunkptr;
+typedef struct malloc_chunk* sbinptr;  /* The type of bins of chunks */
+typedef unsigned int bindex_t;         /* Described below */
+typedef unsigned int binmap_t;         /* Described below */
+typedef unsigned int flag_t;           /* The type of various bit flag sets */
+
+/* ------------------- Chunks sizes and alignments ----------------------- */
+
+#define MCHUNK_SIZE         (sizeof(mchunk))
+
+#if FOOTERS
+#define CHUNK_OVERHEAD      (TWO_SIZE_T_SIZES)
+#else /* FOOTERS */
+#define CHUNK_OVERHEAD      (SIZE_T_SIZE)
+#endif /* FOOTERS */
+
+/* MMapped chunks need a second word of overhead ... */
+#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES)
+/* ... and additional padding for fake next-chunk at foot */
+#define MMAP_FOOT_PAD       (FOUR_SIZE_T_SIZES)
+
+/* The smallest size we can malloc is an aligned minimal chunk */
+#define MIN_CHUNK_SIZE\
+  ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
+
+/* conversion from malloc headers to user pointers, and back */
+#define chunk2mem(p)        ((void*)((char*)(p)       + TWO_SIZE_T_SIZES))
+#define mem2chunk(mem)      ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES))
+/* chunk associated with aligned address A */
+#define align_as_chunk(A)   (mchunkptr)((A) + align_offset(chunk2mem(A)))
+
+/* Bounds on request (not chunk) sizes. */
+#define MAX_REQUEST         ((-MIN_CHUNK_SIZE) << 2)
+#define MIN_REQUEST         (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE)
+
+/* pad request bytes into a usable size */
+#define pad_request(req) \
+   (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
+
+/* pad request, checking for minimum (but not maximum) */
+#define request2size(req) \
+  (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req))
+
+
+/* ------------------ Operations on head and foot fields ----------------- */
+
+/*
+  The head field of a chunk is or'ed with PINUSE_BIT when previous
+  adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in
+  use. If the chunk was obtained with mmap, the prev_foot field has
+  IS_MMAPPED_BIT set, otherwise holding the offset of the base of the
+  mmapped region to the base of the chunk.
+
+  FLAG4_BIT is not used by this malloc, but might be useful in extensions.
+*/
+
+#define PINUSE_BIT          (SIZE_T_ONE)
+#define CINUSE_BIT          (SIZE_T_TWO)
+#define FLAG4_BIT           (SIZE_T_FOUR)
+#define INUSE_BITS          (PINUSE_BIT|CINUSE_BIT)
+#define FLAG_BITS           (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT)
+
+/* Head value for fenceposts */
+#define FENCEPOST_HEAD      (INUSE_BITS|SIZE_T_SIZE)
+
+/* extraction of fields from head words */
+#define cinuse(p)           ((p)->head & CINUSE_BIT)
+#define pinuse(p)           ((p)->head & PINUSE_BIT)
+#define chunksize(p)        ((p)->head & ~(FLAG_BITS))
+
+#define clear_pinuse(p)     ((p)->head &= ~PINUSE_BIT)
+#define clear_cinuse(p)     ((p)->head &= ~CINUSE_BIT)
+
+/* Treat space at ptr +/- offset as a chunk */
+#define chunk_plus_offset(p, s)  ((mchunkptr)(((char*)(p)) + (s)))
+#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s)))
+
+/* Ptr to next or previous physical malloc_chunk. */
+#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS)))
+#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) ))
+
+/* extract next chunk's pinuse bit */
+#define next_pinuse(p)  ((next_chunk(p)->head) & PINUSE_BIT)
+
+/* Get/set size at footer */
+#define get_foot(p, s)  (((mchunkptr)((char*)(p) + (s)))->prev_foot)
+#define set_foot(p, s)  (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s))
+
+/* Set size, pinuse bit, and foot */
+#define set_size_and_pinuse_of_free_chunk(p, s)\
+  ((p)->head = (s|PINUSE_BIT), set_foot(p, s))
+
+/* Set size, pinuse bit, foot, and clear next pinuse */
+#define set_free_with_pinuse(p, s, n)\
+  (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s))
+
+#define is_mmapped(p)\
+  (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_MMAPPED_BIT))
+
+/* Get the internal overhead associated with chunk p */
+#define overhead_for(p)\
+ (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD)
+
+/* Return true if malloced space is not necessarily cleared */
+#if MMAP_CLEARS
+#define calloc_must_clear(p) (!is_mmapped(p))
+#else /* MMAP_CLEARS */
+#define calloc_must_clear(p) (1)
+#endif /* MMAP_CLEARS */
+
+/* ---------------------- Overlaid data structures ----------------------- */
+
+/*
+  When chunks are not in use, they are treated as nodes of either
+  lists or trees.
+
+  "Small"  chunks are stored in circular doubly-linked lists, and look
+  like this:
+
+    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Size of previous chunk                            |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    `head:' |             Size of chunk, in bytes                         |P|
+      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Forward pointer to next chunk in list             |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Back pointer to previous chunk in list            |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Unused space (may be 0 bytes long)                .
+           .                                                               .
+           .                                                               |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    `foot:' |             Size of chunk, in bytes                           |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+  Larger chunks are kept in a form of bitwise digital trees (aka
+  tries) keyed on chunksizes.  Because malloc_tree_chunks are only for
+  free chunks greater than 256 bytes, their size doesn't impose any
+  constraints on user chunk sizes.  Each node looks like:
+
+    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Size of previous chunk                            |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    `head:' |             Size of chunk, in bytes                         |P|
+      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Forward pointer to next chunk of same size        |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Back pointer to previous chunk of same size       |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Pointer to left child (child[0])                  |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Pointer to right child (child[1])                 |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Pointer to parent                                 |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             bin index of this chunk                           |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           |             Unused space                                      .
+           .                                                               |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    `foot:' |             Size of chunk, in bytes                           |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+  Each tree holding treenodes is a tree of unique chunk sizes.  Chunks
+  of the same size are arranged in a circularly-linked list, with only
+  the oldest chunk (the next to be used, in our FIFO ordering)
+  actually in the tree.  (Tree members are distinguished by a non-null
+  parent pointer.)  If a chunk with the same size an an existing node
+  is inserted, it is linked off the existing node using pointers that
+  work in the same way as fd/bk pointers of small chunks.
+
+  Each tree contains a power of 2 sized range of chunk sizes (the
+  smallest is 0x100 <= x < 0x180), which is is divided in half at each
+  tree level, with the chunks in the smaller half of the range (0x100
+  <= x < 0x140 for the top nose) in the left subtree and the larger
+  half (0x140 <= x < 0x180) in the right subtree.  This is, of course,
+  done by inspecting individual bits.
+
+  Using these rules, each node's left subtree contains all smaller
+  sizes than its right subtree.  However, the node at the root of each
+  subtree has no particular ordering relationship to either.  (The
+  dividing line between the subtree sizes is based on trie relation.)
+  If we remove the last chunk of a given size from the interior of the
+  tree, we need to replace it with a leaf node.  The tree ordering
+  rules permit a node to be replaced by any leaf below it.
+
+  The smallest chunk in a tree (a common operation in a best-fit
+  allocator) can be found by walking a path to the leftmost leaf in
+  the tree.  Unlike a usual binary tree, where we follow left child
+  pointers until we reach a null, here we follow the right child
+  pointer any time the left one is null, until we reach a leaf with
+  both child pointers null. The smallest chunk in the tree will be
+  somewhere along that path.
+
+  The worst case number of steps to add, find, or remove a node is
+  bounded by the number of bits differentiating chunks within
+  bins. Under current bin calculations, this ranges from 6 up to 21
+  (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case
+  is of course much better.
+*/
+
+struct malloc_tree_chunk {
+  /* The first four fields must be compatible with malloc_chunk */
+  size_t                    prev_foot;
+  size_t                    head;
+  struct malloc_tree_chunk* fd;
+  struct malloc_tree_chunk* bk;
+
+  struct malloc_tree_chunk* child[2];
+  struct malloc_tree_chunk* parent;
+  bindex_t                  index;
+};
+
+typedef struct malloc_tree_chunk  tchunk;
+typedef struct malloc_tree_chunk* tchunkptr;
+typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */
+
+/* A little helper macro for trees */
+#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1])
+
+/* ----------------------------- Segments -------------------------------- */
+
+/*
+  Each malloc space may include non-contiguous segments, held in a
+  list headed by an embedded malloc_segment record representing the
+  top-most space. Segments also include flags holding properties of
+  the space. Large chunks that are directly allocated by mmap are not
+  included in this list. They are instead independently created and
+  destroyed without otherwise keeping track of them.
+
+  Segment management mainly comes into play for spaces allocated by
+  MMAP.  Any call to MMAP might or might not return memory that is
+  adjacent to an existing segment.  MORECORE normally contiguously
+  extends the current space, so this space is almost always adjacent,
+  which is simpler and faster to deal with. (This is why MORECORE is
+  used preferentially to MMAP when both are available -- see
+  sys_alloc.)  When allocating using MMAP, we don't use any of the
+  hinting mechanisms (inconsistently) supported in various
+  implementations of unix mmap, or distinguish reserving from
+  committing memory. Instead, we just ask for space, and exploit
+  contiguity when we get it.  It is probably possible to do
+  better than this on some systems, but no general scheme seems
+  to be significantly better.
+
+  Management entails a simpler variant of the consolidation scheme
+  used for chunks to reduce fragmentation -- new adjacent memory is
+  normally prepended or appended to an existing segment. However,
+  there are limitations compared to chunk consolidation that mostly
+  reflect the fact that segment processing is relatively infrequent
+  (occurring only when getting memory from system) and that we
+  don't expect to have huge numbers of segments:
+
+  * Segments are not indexed, so traversal requires linear scans.  (It
+    would be possible to index these, but is not worth the extra
+    overhead and complexity for most programs on most platforms.)
+  * New segments are only appended to old ones when holding top-most
+    memory; if they cannot be prepended to others, they are held in
+    different segments.
+
+  Except for the top-most segment of an mstate, each segment record
+  is kept at the tail of its segment. Segments are added by pushing
+  segment records onto the list headed by &mstate.seg for the
+  containing mstate.
+
+  Segment flags control allocation/merge/deallocation policies:
+  * If EXTERN_BIT set, then we did not allocate this segment,
+    and so should not try to deallocate or merge with others.
+    (This currently holds only for the initial segment passed
+    into create_mspace_with_base.)
+  * If IS_MMAPPED_BIT set, the segment may be merged with
+    other surrounding mmapped segments and trimmed/de-allocated
+    using munmap.
+  * If neither bit is set, then the segment was obtained using
+    MORECORE so can be merged with surrounding MORECORE'd segments
+    and deallocated/trimmed using MORECORE with negative arguments.
+*/
+
+struct malloc_segment {
+  char*        base;             /* base address */
+  size_t       size;             /* allocated size */
+  struct malloc_segment* next;   /* ptr to next segment */
+  flag_t       sflags;           /* mmap and extern flag */
+};
+
+#define is_mmapped_segment(S)  ((S)->sflags & IS_MMAPPED_BIT)
+#define is_extern_segment(S)   ((S)->sflags & EXTERN_BIT)
+
+typedef struct malloc_segment  msegment;
+typedef struct malloc_segment* msegmentptr;
+
+/* ---------------------------- malloc_state ----------------------------- */
+
+/*
+   A malloc_state holds all of the bookkeeping for a space.
+   The main fields are:
+
+  Top
+    The topmost chunk of the currently active segment. Its size is
+    cached in topsize.  The actual size of topmost space is
+    topsize+TOP_FOOT_SIZE, which includes space reserved for adding
+    fenceposts and segment records if necessary when getting more
+    space from the system.  The size at which to autotrim top is
+    cached from mparams in trim_check, except that it is disabled if
+    an autotrim fails.
+
+  Designated victim (dv)
+    This is the preferred chunk for servicing small requests that
+    don't have exact fits.  It is normally the chunk split off most
+    recently to service another small request.  Its size is cached in
+    dvsize. The link fields of this chunk are not maintained since it
+    is not kept in a bin.
+
+  SmallBins
+    An array of bin headers for free chunks.  These bins hold chunks
+    with sizes less than MIN_LARGE_SIZE bytes. Each bin contains
+    chunks of all the same size, spaced 8 bytes apart.  To simplify
+    use in double-linked lists, each bin header acts as a malloc_chunk
+    pointing to the real first node, if it exists (else pointing to
+    itself).  This avoids special-casing for headers.  But to avoid
+    waste, we allocate only the fd/bk pointers of bins, and then use
+    repositioning tricks to treat these as the fields of a chunk.
+
+  TreeBins
+    Treebins are pointers to the roots of trees holding a range of
+    sizes. There are 2 equally spaced treebins for each power of two
+    from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything
+    larger.
+
+  Bin maps
+    There is one bit map for small bins ("smallmap") and one for
+    treebins ("treemap).  Each bin sets its bit when non-empty, and
+    clears the bit when empty.  Bit operations are then used to avoid
+    bin-by-bin searching -- nearly all "search" is done without ever
+    looking at bins that won't be selected.  The bit maps
+    conservatively use 32 bits per map word, even if on 64bit system.
+    For a good description of some of the bit-based techniques used
+    here, see Henry S. Warren Jr's book "Hacker's Delight" (and
+    supplement at http://hackersdelight.org/). Many of these are
+    intended to reduce the branchiness of paths through malloc etc, as
+    well as to reduce the number of memory locations read or written.
+
+  Segments
+    A list of segments headed by an embedded malloc_segment record
+    representing the initial space.
+
+  Address check support
+    The least_addr field is the least address ever obtained from
+    MORECORE or MMAP. Attempted frees and reallocs of any address less
+    than this are trapped (unless INSECURE is defined).
+
+  Magic tag
+    A cross-check field that should always hold same value as mparams.magic.
+
+  Flags
+    Bits recording whether to use MMAP, locks, or contiguous MORECORE
+
+  Statistics
+    Each space keeps track of current and maximum system memory
+    obtained via MORECORE or MMAP.
+
+  Trim support
+    Fields holding the amount of unused topmost memory that should trigger
+    timming, and a counter to force periodic scanning to release unused
+    non-topmost segments.
+
+  Locking
+    If USE_LOCKS is defined, the "mutex" lock is acquired and released
+    around every public call using this mspace.
+
+  Extension support
+    A void* pointer and a size_t field that can be used to help implement
+    extensions to this malloc.
+*/
+
+/* Bin types, widths and sizes */
+#define NSMALLBINS        (32U)
+#define NTREEBINS         (32U)
+#define SMALLBIN_SHIFT    (3U)
+#define SMALLBIN_WIDTH    (SIZE_T_ONE << SMALLBIN_SHIFT)
+#define TREEBIN_SHIFT     (8U)
+#define MIN_LARGE_SIZE    (SIZE_T_ONE << TREEBIN_SHIFT)
+#define MAX_SMALL_SIZE    (MIN_LARGE_SIZE - SIZE_T_ONE)
+#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD)
+
+struct malloc_state {
+  binmap_t   smallmap;
+  binmap_t   treemap;
+  size_t     dvsize;
+  size_t     topsize;
+  char*      least_addr;
+  mchunkptr  dv;
+  mchunkptr  top;
+  size_t     trim_check;
+  size_t     release_checks;
+  size_t     magic;
+  mchunkptr  smallbins[(NSMALLBINS+1)*2];
+  tbinptr    treebins[NTREEBINS];
+  size_t     footprint;
+  size_t     max_footprint;
+  flag_t     mflags;
+#if USE_LOCKS
+  MLOCK_T    mutex;     /* locate lock among fields that rarely change */
+#endif /* USE_LOCKS */
+  msegment   seg;
+  void*      extp;      /* Unused but available for extensions */
+  size_t     exts;
+};
+
+typedef struct malloc_state*    mstate;
+
+/* ------------- Global malloc_state and malloc_params ------------------- */
+
+/*
+  malloc_params holds global properties, including those that can be
+  dynamically set using mallopt. There is a single instance, mparams,
+  initialized in init_mparams. Note that the non-zeroness of "magic"
+  also serves as an initialization flag.
+*/
+
+struct malloc_params {
+  volatile size_t magic;
+  size_t page_size;
+  size_t granularity;
+  size_t mmap_threshold;
+  size_t trim_threshold;
+  flag_t default_mflags;
+};
+
+static struct malloc_params mparams;
+
+/* Ensure mparams initialized */
+#define ensure_initialization() ((void)(mparams.magic != 0 || init_mparams()))
+
+#if !ONLY_MSPACES
+
+/* The global malloc_state used for all non-"mspace" calls */
+static struct malloc_state _gm_;
+#define gm                 (&_gm_)
+#define is_global(M)       ((M) == &_gm_)
+
+#endif /* !ONLY_MSPACES */
+
+#define is_initialized(M)  ((M)->top != 0)
+
+/* -------------------------- system alloc setup ------------------------- */
+
+/* Operations on mflags */
+
+#define use_lock(M)           ((M)->mflags &   USE_LOCK_BIT)
+#define enable_lock(M)        ((M)->mflags |=  USE_LOCK_BIT)
+#define disable_lock(M)       ((M)->mflags &= ~USE_LOCK_BIT)
+
+#define use_mmap(M)           ((M)->mflags &   USE_MMAP_BIT)
+#define enable_mmap(M)        ((M)->mflags |=  USE_MMAP_BIT)
+#define disable_mmap(M)       ((M)->mflags &= ~USE_MMAP_BIT)
+
+#define use_noncontiguous(M)  ((M)->mflags &   USE_NONCONTIGUOUS_BIT)
+#define disable_contiguous(M) ((M)->mflags |=  USE_NONCONTIGUOUS_BIT)
+
+#define set_lock(M,L)\
+ ((M)->mflags = (L)?\
+  ((M)->mflags | USE_LOCK_BIT) :\
+  ((M)->mflags & ~USE_LOCK_BIT))
+
+/* page-align a size */
+#define page_align(S)\
+ (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE))
+
+/* granularity-align a size */
+#define granularity_align(S)\
+  (((S) + (mparams.granularity - SIZE_T_ONE))\
+   & ~(mparams.granularity - SIZE_T_ONE))
+
+
+/* For mmap, use granularity alignment on windows, else page-align */
+#ifdef WIN32
+#define mmap_align(S) granularity_align(S)
+#else
+#define mmap_align(S) page_align(S)
+#endif
+
+/* For sys_alloc, enough padding to ensure can malloc request on success */
+#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT)
+
+#define is_page_aligned(S)\
+   (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0)
+#define is_granularity_aligned(S)\
+   (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0)
+
+/*  True if segment S holds address A */
+#define segment_holds(S, A)\
+  ((char*)(A) >= S->base && (char*)(A) < S->base + S->size)
+
+/* Return segment holding given address */
+static msegmentptr segment_holding(mstate m, char* addr) {
+  msegmentptr sp = &m->seg;
+  for (;;) {
+    if (addr >= sp->base && addr < sp->base + sp->size)
+      return sp;
+    if ((sp = sp->next) == 0)
+      return 0;
+  }
+}
+
+/* Return true if segment contains a segment link */
+static int has_segment_link(mstate m, msegmentptr ss) {
+  msegmentptr sp = &m->seg;
+  for (;;) {
+    if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size)
+      return 1;
+    if ((sp = sp->next) == 0)
+      return 0;
+  }
+}
+
+#ifndef MORECORE_CANNOT_TRIM
+#define should_trim(M,s)  ((s) > (M)->trim_check)
+#else  /* MORECORE_CANNOT_TRIM */
+#define should_trim(M,s)  (0)
+#endif /* MORECORE_CANNOT_TRIM */
+
+/*
+  TOP_FOOT_SIZE is padding at the end of a segment, including space
+  that may be needed to place segment records and fenceposts when new
+  noncontiguous segments are added.
+*/
+#define TOP_FOOT_SIZE\
+  (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE)
+
+
+/* -------------------------------  Hooks -------------------------------- */
+
+/*
+  PREACTION should be defined to return 0 on success, and nonzero on
+  failure. If you are not using locking, you can redefine these to do
+  anything you like.
+*/
+
+#if USE_LOCKS
+
+#define PREACTION(M)  ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0)
+#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); }
+#else /* USE_LOCKS */
+
+#ifndef PREACTION
+#define PREACTION(M) (0)
+#endif  /* PREACTION */
+
+#ifndef POSTACTION
+#define POSTACTION(M)
+#endif  /* POSTACTION */
+
+#endif /* USE_LOCKS */
+
+/*
+  CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses.
+  USAGE_ERROR_ACTION is triggered on detected bad frees and
+  reallocs. The argument p is an address that might have triggered the
+  fault. It is ignored by the two predefined actions, but might be
+  useful in custom actions that try to help diagnose errors.
+*/
+
+#if PROCEED_ON_ERROR
+
+/* A count of the number of corruption errors causing resets */
+int malloc_corruption_error_count;
+
+/* default corruption action */
+static void reset_on_error(mstate m);
+
+#define CORRUPTION_ERROR_ACTION(m)  reset_on_error(m)
+#define USAGE_ERROR_ACTION(m, p)
+
+#else /* PROCEED_ON_ERROR */
+
+#ifndef CORRUPTION_ERROR_ACTION
+#define CORRUPTION_ERROR_ACTION(m) ABORT
+#endif /* CORRUPTION_ERROR_ACTION */
+
+#ifndef USAGE_ERROR_ACTION
+#define USAGE_ERROR_ACTION(m,p) ABORT
+#endif /* USAGE_ERROR_ACTION */
+
+#endif /* PROCEED_ON_ERROR */
+
+/* -------------------------- Debugging setup ---------------------------- */
+
+#if ! DEBUG
+
+#define check_free_chunk(M,P)
+#define check_inuse_chunk(M,P)
+#define check_malloced_chunk(M,P,N)
+#define check_mmapped_chunk(M,P)
+#define check_malloc_state(M)
+#define check_top_chunk(M,P)
+
+#else /* DEBUG */
+#define check_free_chunk(M,P)       do_check_free_chunk(M,P)
+#define check_inuse_chunk(M,P)      do_check_inuse_chunk(M,P)
+#define check_top_chunk(M,P)        do_check_top_chunk(M,P)
+#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N)
+#define check_mmapped_chunk(M,P)    do_check_mmapped_chunk(M,P)
+#define check_malloc_state(M)       do_check_malloc_state(M)
+
+static void   do_check_any_chunk(mstate m, mchunkptr p);
+static void   do_check_top_chunk(mstate m, mchunkptr p);
+static void   do_check_mmapped_chunk(mstate m, mchunkptr p);
+static void   do_check_inuse_chunk(mstate m, mchunkptr p);
+static void   do_check_free_chunk(mstate m, mchunkptr p);
+static void   do_check_malloced_chunk(mstate m, void* mem, size_t s);
+static void   do_check_tree(mstate m, tchunkptr t);
+static void   do_check_treebin(mstate m, bindex_t i);
+static void   do_check_smallbin(mstate m, bindex_t i);
+static void   do_check_malloc_state(mstate m);
+static int    bin_find(mstate m, mchunkptr x);
+static size_t traverse_and_check(mstate m);
+#endif /* DEBUG */
+
+/* ---------------------------- Indexing Bins ---------------------------- */
+
+#define is_small(s)         (((s) >> SMALLBIN_SHIFT) < NSMALLBINS)
+#define small_index(s)      ((s)  >> SMALLBIN_SHIFT)
+#define small_index2size(i) ((i)  << SMALLBIN_SHIFT)
+#define MIN_SMALL_INDEX     (small_index(MIN_CHUNK_SIZE))
+
+/* addressing by index. See above about smallbin repositioning */
+#define smallbin_at(M, i)   ((sbinptr)((char*)&((M)->smallbins[(i)<<1])))
+#define treebin_at(M,i)     (&((M)->treebins[i]))
+
+/* assign tree index for size S to variable I. Use x86 asm if possible  */
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#define compute_tree_index(S, I)\
+{\
+  unsigned int X = S >> TREEBIN_SHIFT;\
+  if (X == 0)\
+    I = 0;\
+  else if (X > 0xFFFF)\
+    I = NTREEBINS-1;\
+  else {\
+    unsigned int K;\
+    __asm__("bsrl\t%1, %0\n\t" : "=r" (K) : "rm"  (X));\
+    I =  (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
+  }\
+}
+
+#elif defined (__INTEL_COMPILER)
+#define compute_tree_index(S, I)\
+{\
+  size_t X = S >> TREEBIN_SHIFT;\
+  if (X == 0)\
+    I = 0;\
+  else if (X > 0xFFFF)\
+    I = NTREEBINS-1;\
+  else {\
+    unsigned int K = _bit_scan_reverse (X); \
+    I =  (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
+  }\
+}
+
+#elif defined(_MSC_VER) && _MSC_VER>=1300
+#define compute_tree_index(S, I)\
+{\
+  size_t X = S >> TREEBIN_SHIFT;\
+  if (X == 0)\
+    I = 0;\
+  else if (X > 0xFFFF)\
+    I = NTREEBINS-1;\
+  else {\
+    unsigned int K;\
+    _BitScanReverse((DWORD *) &K, X);\
+    I =  (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
+  }\
+}
+
+#else /* GNUC */
+#define compute_tree_index(S, I)\
+{\
+  size_t X = S >> TREEBIN_SHIFT;\
+  if (X == 0)\
+    I = 0;\
+  else if (X > 0xFFFF)\
+    I = NTREEBINS-1;\
+  else {\
+    unsigned int Y = (unsigned int)X;\
+    unsigned int N = ((Y - 0x100) >> 16) & 8;\
+    unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\
+    N += K;\
+    N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\
+    K = 14 - N + ((Y <<= K) >> 15);\
+    I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\
+  }\
+}
+#endif /* GNUC */
+
+/* Bit representing maximum resolved size in a treebin at i */
+#define bit_for_tree_index(i) \
+   (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2)
+
+/* Shift placing maximum resolved bit in a treebin at i as sign bit */
+#define leftshift_for_tree_index(i) \
+   ((i == NTREEBINS-1)? 0 : \
+    ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2)))
+
+/* The size of the smallest chunk held in bin with index i */
+#define minsize_for_tree_index(i) \
+   ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) |  \
+   (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1)))
+
+
+/* ------------------------ Operations on bin maps ----------------------- */
+
+/* bit corresponding to given index */
+#define idx2bit(i)              ((binmap_t)(1) << (i))
+
+/* Mark/Clear bits with given index */
+#define mark_smallmap(M,i)      ((M)->smallmap |=  idx2bit(i))
+#define clear_smallmap(M,i)     ((M)->smallmap &= ~idx2bit(i))
+#define smallmap_is_marked(M,i) ((M)->smallmap &   idx2bit(i))
+
+#define mark_treemap(M,i)       ((M)->treemap  |=  idx2bit(i))
+#define clear_treemap(M,i)      ((M)->treemap  &= ~idx2bit(i))
+#define treemap_is_marked(M,i)  ((M)->treemap  &   idx2bit(i))
+
+/* isolate the least set bit of a bitmap */
+#define least_bit(x)         ((x) & -(x))
+
+/* mask with all bits to left of least bit of x on */
+#define left_bits(x)         ((x<<1) | -(x<<1))
+
+/* mask with all bits to left of or equal to least bit of x on */
+#define same_or_left_bits(x) ((x) | -(x))
+
+/* index corresponding to given bit. Use x86 asm if possible */
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#define compute_bit2idx(X, I)\
+{\
+  unsigned int J;\
+  __asm__("bsfl\t%1, %0\n\t" : "=r" (J) : "rm" (X));\
+  I = (bindex_t)J;\
+}
+
+#elif defined (__INTEL_COMPILER)
+#define compute_bit2idx(X, I)\
+{\
+  unsigned int J;\
+  J = _bit_scan_forward (X); \
+  I = (bindex_t)J;\
+}
+
+#elif defined(_MSC_VER) && _MSC_VER>=1300
+#define compute_bit2idx(X, I)\
+{\
+  unsigned int J;\
+  _BitScanForward((DWORD *) &J, X);\
+  I = (bindex_t)J;\
+}
+
+#elif USE_BUILTIN_FFS
+#define compute_bit2idx(X, I) I = ffs(X)-1
+
+#else
+#define compute_bit2idx(X, I)\
+{\
+  unsigned int Y = X - 1;\
+  unsigned int K = Y >> (16-4) & 16;\
+  unsigned int N = K;        Y >>= K;\
+  N += K = Y >> (8-3) &  8;  Y >>= K;\
+  N += K = Y >> (4-2) &  4;  Y >>= K;\
+  N += K = Y >> (2-1) &  2;  Y >>= K;\
+  N += K = Y >> (1-0) &  1;  Y >>= K;\
+  I = (bindex_t)(N + Y);\
+}
+#endif /* GNUC */
+
+
+/* ----------------------- Runtime Check Support ------------------------- */
+
+/*
+  For security, the main invariant is that malloc/free/etc never
+  writes to a static address other than malloc_state, unless static
+  malloc_state itself has been corrupted, which cannot occur via
+  malloc (because of these checks). In essence this means that we
+  believe all pointers, sizes, maps etc held in malloc_state, but
+  check all of those linked or offsetted from other embedded data
+  structures.  These checks are interspersed with main code in a way
+  that tends to minimize their run-time cost.
+
+  When FOOTERS is defined, in addition to range checking, we also
+  verify footer fields of inuse chunks, which can be used guarantee
+  that the mstate controlling malloc/free is intact.  This is a
+  streamlined version of the approach described by William Robertson
+  et al in "Run-time Detection of Heap-based Overflows" LISA'03
+  http://www.usenix.org/events/lisa03/tech/robertson.html The footer
+  of an inuse chunk holds the xor of its mstate and a random seed,
+  that is checked upon calls to free() and realloc().  This is
+  (probablistically) unguessable from outside the program, but can be
+  computed by any code successfully malloc'ing any chunk, so does not
+  itself provide protection against code that has already broken
+  security through some other means.  Unlike Robertson et al, we
+  always dynamically check addresses of all offset chunks (previous,
+  next, etc). This turns out to be cheaper than relying on hashes.
+*/
+
+#if !INSECURE
+/* Check if address a is at least as high as any from MORECORE or MMAP */
+#define ok_address(M, a) ((char*)(a) >= (M)->least_addr)
+/* Check if address of next chunk n is higher than base chunk p */
+#define ok_next(p, n)    ((char*)(p) < (char*)(n))
+/* Check if p has its cinuse bit on */
+#define ok_cinuse(p)     cinuse(p)
+/* Check if p has its pinuse bit on */
+#define ok_pinuse(p)     pinuse(p)
+
+#else /* !INSECURE */
+#define ok_address(M, a) (1)
+#define ok_next(b, n)    (1)
+#define ok_cinuse(p)     (1)
+#define ok_pinuse(p)     (1)
+#endif /* !INSECURE */
+
+#if (FOOTERS && !INSECURE)
+/* Check if (alleged) mstate m has expected magic field */
+#define ok_magic(M)      ((M)->magic == mparams.magic)
+#else  /* (FOOTERS && !INSECURE) */
+#define ok_magic(M)      (1)
+#endif /* (FOOTERS && !INSECURE) */
+
+
+/* In gcc, use __builtin_expect to minimize impact of checks */
+#if !INSECURE
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define RTCHECK(e)  __builtin_expect(e, 1)
+#else /* GNUC */
+#define RTCHECK(e)  (e)
+#endif /* GNUC */
+#else /* !INSECURE */
+#define RTCHECK(e)  (1)
+#endif /* !INSECURE */
+
+/* macros to set up inuse chunks with or without footers */
+
+#if !FOOTERS
+
+#define mark_inuse_foot(M,p,s)
+
+/* Set cinuse bit and pinuse bit of next chunk */
+#define set_inuse(M,p,s)\
+  ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
+  ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
+
+/* Set cinuse and pinuse of this chunk and pinuse of next chunk */
+#define set_inuse_and_pinuse(M,p,s)\
+  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+  ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
+
+/* Set size, cinuse and pinuse bit of this chunk */
+#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
+  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT))
+
+#else /* FOOTERS */
+
+/* Set foot of inuse chunk to be xor of mstate and seed */
+#define mark_inuse_foot(M,p,s)\
+  (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic))
+
+#define get_mstate_for(p)\
+  ((mstate)(((mchunkptr)((char*)(p) +\
+    (chunksize(p))))->prev_foot ^ mparams.magic))
+
+#define set_inuse(M,p,s)\
+  ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
+  (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \
+  mark_inuse_foot(M,p,s))
+
+#define set_inuse_and_pinuse(M,p,s)\
+  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+  (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\
+ mark_inuse_foot(M,p,s))
+
+#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
+  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+  mark_inuse_foot(M, p, s))
+
+#endif /* !FOOTERS */
+
+/* ---------------------------- setting mparams -------------------------- */
+
+/* Initialize mparams */
+static int init_mparams(void) {
+#ifdef NEED_GLOBAL_LOCK_INIT
+  if (malloc_global_mutex_status <= 0)
+    init_malloc_global_mutex();
+#endif
+
+  ACQUIRE_MALLOC_GLOBAL_LOCK();
+  if (mparams.magic == 0) {
+    size_t magic;
+    size_t psize;
+    size_t gsize;
+
+#ifndef WIN32
+    psize = malloc_getpagesize;
+    gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize);
+#else /* WIN32 */
+    {
+      SYSTEM_INFO system_info;
+      GetSystemInfo(&system_info);
+      psize = system_info.dwPageSize;
+      gsize = ((DEFAULT_GRANULARITY != 0)?
+              DEFAULT_GRANULARITY : system_info.dwAllocationGranularity);
+    }
+#endif /* WIN32 */
+
+    /* Sanity-check configuration:
+       size_t must be unsigned and as wide as pointer type.
+       ints must be at least 4 bytes.
+       alignment must be at least 8.
+       Alignment, min chunk size, and page size must all be powers of 2.
+    */
+    if ((sizeof(size_t) != sizeof(char*)) ||
+       (MAX_SIZE_T < MIN_CHUNK_SIZE)  ||
+       (sizeof(int) < 4)  ||
+       (MALLOC_ALIGNMENT < (size_t)8U) ||
+       ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) ||
+       ((MCHUNK_SIZE      & (MCHUNK_SIZE-SIZE_T_ONE))      != 0) ||
+       ((gsize            & (gsize-SIZE_T_ONE))            != 0) ||
+       ((psize            & (psize-SIZE_T_ONE))            != 0))
+      ABORT;
+
+    mparams.granularity = gsize;
+    mparams.page_size = psize;
+    mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD;
+    mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD;
+#if MORECORE_CONTIGUOUS
+    mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT;
+#else  /* MORECORE_CONTIGUOUS */
+    mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT;
+#endif /* MORECORE_CONTIGUOUS */
+
+#if !ONLY_MSPACES
+    /* Set up lock for main malloc area */
+    gm->mflags = mparams.default_mflags;
+    INITIAL_LOCK(&gm->mutex);
+#endif
+
+#if (FOOTERS && !INSECURE)
+    {
+#if USE_DEV_RANDOM
+      int fd;
+      unsigned char buf[sizeof(size_t)];
+      /* Try to use /dev/urandom, else fall back on using time */
+      if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 &&
+         read(fd, buf, sizeof(buf)) == sizeof(buf)) {
+       magic = *((size_t *) buf);
+       close(fd);
+      }
+      else
+#endif /* USE_DEV_RANDOM */
+#ifdef WIN32
+       magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U);
+#else
+      magic = (size_t)(time(0) ^ (size_t)0x55555555U);
+#endif
+      magic |= (size_t)8U;    /* ensure nonzero */
+      magic &= ~(size_t)7U;   /* improve chances of fault for bad values */
+    }
+#else /* (FOOTERS && !INSECURE) */
+    magic = (size_t)0x58585858U;
+#endif /* (FOOTERS && !INSECURE) */
+
+    mparams.magic = magic;
+  }
+
+  RELEASE_MALLOC_GLOBAL_LOCK();
+  return 1;
+}
+
+/* support for mallopt */
+static int change_mparam(int param_number, int value) {
+  size_t val = (value == -1)? MAX_SIZE_T : (size_t)value;
+  ensure_initialization();
+  switch(param_number) {
+  case M_TRIM_THRESHOLD:
+    mparams.trim_threshold = val;
+    return 1;
+  case M_GRANULARITY:
+    if (val >= mparams.page_size && ((val & (val-1)) == 0)) {
+      mparams.granularity = val;
+      return 1;
+    }
+    else
+      return 0;
+  case M_MMAP_THRESHOLD:
+    mparams.mmap_threshold = val;
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+#if DEBUG
+/* ------------------------- Debugging Support --------------------------- */
+
+/* Check properties of any chunk, whether free, inuse, mmapped etc  */
+static void do_check_any_chunk(mstate m, mchunkptr p) {
+  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+  assert(ok_address(m, p));
+}
+
+/* Check properties of top chunk */
+static void do_check_top_chunk(mstate m, mchunkptr p) {
+  msegmentptr sp = segment_holding(m, (char*)p);
+  size_t  sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */
+  assert(sp != 0);
+  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+  assert(ok_address(m, p));
+  assert(sz == m->topsize);
+  assert(sz > 0);
+  assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE);
+  assert(pinuse(p));
+  assert(!pinuse(chunk_plus_offset(p, sz)));
+}
+
+/* Check properties of (inuse) mmapped chunks */
+static void do_check_mmapped_chunk(mstate m, mchunkptr p) {
+  size_t  sz = chunksize(p);
+  size_t len = (sz + (p->prev_foot & ~IS_MMAPPED_BIT) + MMAP_FOOT_PAD);
+  assert(is_mmapped(p));
+  assert(use_mmap(m));
+  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+  assert(ok_address(m, p));
+  assert(!is_small(sz));
+  assert((len & (mparams.page_size-SIZE_T_ONE)) == 0);
+  assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD);
+  assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0);
+}
+
+/* Check properties of inuse chunks */
+static void do_check_inuse_chunk(mstate m, mchunkptr p) {
+  do_check_any_chunk(m, p);
+  assert(cinuse(p));
+  assert(next_pinuse(p));
+  /* If not pinuse and not mmapped, previous chunk has OK offset */
+  assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p);
+  if (is_mmapped(p))
+    do_check_mmapped_chunk(m, p);
+}
+
+/* Check properties of free chunks */
+static void do_check_free_chunk(mstate m, mchunkptr p) {
+  size_t sz = chunksize(p);
+  mchunkptr next = chunk_plus_offset(p, sz);
+  do_check_any_chunk(m, p);
+  assert(!cinuse(p));
+  assert(!next_pinuse(p));
+  assert (!is_mmapped(p));
+  if (p != m->dv && p != m->top) {
+    if (sz >= MIN_CHUNK_SIZE) {
+      assert((sz & CHUNK_ALIGN_MASK) == 0);
+      assert(is_aligned(chunk2mem(p)));
+      assert(next->prev_foot == sz);
+      assert(pinuse(p));
+      assert (next == m->top || cinuse(next));
+      assert(p->fd->bk == p);
+      assert(p->bk->fd == p);
+    }
+    else  /* markers are always of size SIZE_T_SIZE */
+      assert(sz == SIZE_T_SIZE);
+  }
+}
+
+/* Check properties of malloced chunks at the point they are malloced */
+static void do_check_malloced_chunk(mstate m, void* mem, size_t s) {
+  if (mem != 0) {
+    mchunkptr p = mem2chunk(mem);
+    size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT);
+    do_check_inuse_chunk(m, p);
+    assert((sz & CHUNK_ALIGN_MASK) == 0);
+    assert(sz >= MIN_CHUNK_SIZE);
+    assert(sz >= s);
+    /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */
+    assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE));
+  }
+}
+
+/* Check a tree and its subtrees.  */
+static void do_check_tree(mstate m, tchunkptr t) {
+  tchunkptr head = 0;
+  tchunkptr u = t;
+  bindex_t tindex = t->index;
+  size_t tsize = chunksize(t);
+  bindex_t idx;
+  compute_tree_index(tsize, idx);
+  assert(tindex == idx);
+  assert(tsize >= MIN_LARGE_SIZE);
+  assert(tsize >= minsize_for_tree_index(idx));
+  assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1))));
+
+  do { /* traverse through chain of same-sized nodes */
+    do_check_any_chunk(m, ((mchunkptr)u));
+    assert(u->index == tindex);
+    assert(chunksize(u) == tsize);
+    assert(!cinuse(u));
+    assert(!next_pinuse(u));
+    assert(u->fd->bk == u);
+    assert(u->bk->fd == u);
+    if (u->parent == 0) {
+      assert(u->child[0] == 0);
+      assert(u->child[1] == 0);
+    }
+    else {
+      assert(head == 0); /* only one node on chain has parent */
+      head = u;
+      assert(u->parent != u);
+      assert (u->parent->child[0] == u ||
+             u->parent->child[1] == u ||
+             *((tbinptr*)(u->parent)) == u);
+      if (u->child[0] != 0) {
+       assert(u->child[0]->parent == u);
+       assert(u->child[0] != u);
+       do_check_tree(m, u->child[0]);
+      }
+      if (u->child[1] != 0) {
+       assert(u->child[1]->parent == u);
+       assert(u->child[1] != u);
+       do_check_tree(m, u->child[1]);
+      }
+      if (u->child[0] != 0 && u->child[1] != 0) {
+       assert(chunksize(u->child[0]) < chunksize(u->child[1]));
+      }
+    }
+    u = u->fd;
+  } while (u != t);
+  assert(head != 0);
+}
+
+/*  Check all the chunks in a treebin.  */
+static void do_check_treebin(mstate m, bindex_t i) {
+  tbinptr* tb = treebin_at(m, i);
+  tchunkptr t = *tb;
+  int empty = (m->treemap & (1U << i)) == 0;
+  if (t == 0)
+    assert(empty);
+  if (!empty)
+    do_check_tree(m, t);
+}
+
+/*  Check all the chunks in a smallbin.  */
+static void do_check_smallbin(mstate m, bindex_t i) {
+  sbinptr b = smallbin_at(m, i);
+  mchunkptr p = b->bk;
+  unsigned int empty = (m->smallmap & (1U << i)) == 0;
+  if (p == b)
+    assert(empty);
+  if (!empty) {
+    for (; p != b; p = p->bk) {
+      size_t size = chunksize(p);
+      mchunkptr q;
+      /* each chunk claims to be free */
+      do_check_free_chunk(m, p);
+      /* chunk belongs in bin */
+      assert(small_index(size) == i);
+      assert(p->bk == b || chunksize(p->bk) == chunksize(p));
+      /* chunk is followed by an inuse chunk */
+      q = next_chunk(p);
+      if (q->head != FENCEPOST_HEAD)
+       do_check_inuse_chunk(m, q);
+    }
+  }
+}
+
+/* Find x in a bin. Used in other check functions. */
+static int bin_find(mstate m, mchunkptr x) {
+  size_t size = chunksize(x);
+  if (is_small(size)) {
+    bindex_t sidx = small_index(size);
+    sbinptr b = smallbin_at(m, sidx);
+    if (smallmap_is_marked(m, sidx)) {
+      mchunkptr p = b;
+      do {
+       if (p == x)
+         return 1;
+      } while ((p = p->fd) != b);
+    }
+  }
+  else {
+    bindex_t tidx;
+    compute_tree_index(size, tidx);
+    if (treemap_is_marked(m, tidx)) {
+      tchunkptr t = *treebin_at(m, tidx);
+      size_t sizebits = size << leftshift_for_tree_index(tidx);
+      while (t != 0 && chunksize(t) != size) {
+       t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
+       sizebits <<= 1;
+      }
+      if (t != 0) {
+       tchunkptr u = t;
+       do {
+         if (u == (tchunkptr)x)
+           return 1;
+       } while ((u = u->fd) != t);
+      }
+    }
+  }
+  return 0;
+}
+
+/* Traverse each chunk and check it; return total */
+static size_t traverse_and_check(mstate m) {
+  size_t sum = 0;
+  if (is_initialized(m)) {
+    msegmentptr s = &m->seg;
+    sum += m->topsize + TOP_FOOT_SIZE;
+    while (s != 0) {
+      mchunkptr q = align_as_chunk(s->base);
+      mchunkptr lastq = 0;
+      assert(pinuse(q));
+      while (segment_holds(s, q) &&
+            q != m->top && q->head != FENCEPOST_HEAD) {
+       sum += chunksize(q);
+       if (cinuse(q)) {
+         assert(!bin_find(m, q));
+         do_check_inuse_chunk(m, q);
+       }
+       else {
+         assert(q == m->dv || bin_find(m, q));
+         assert(lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */
+         do_check_free_chunk(m, q);
+       }
+       lastq = q;
+       q = next_chunk(q);
+      }
+      s = s->next;
+    }
+  }
+  return sum;
+}
+
+/* Check all properties of malloc_state. */
+static void do_check_malloc_state(mstate m) {
+  bindex_t i;
+  size_t total;
+  /* check bins */
+  for (i = 0; i < NSMALLBINS; ++i)
+    do_check_smallbin(m, i);
+  for (i = 0; i < NTREEBINS; ++i)
+    do_check_treebin(m, i);
+
+  if (m->dvsize != 0) { /* check dv chunk */
+    do_check_any_chunk(m, m->dv);
+    assert(m->dvsize == chunksize(m->dv));
+    assert(m->dvsize >= MIN_CHUNK_SIZE);
+    assert(bin_find(m, m->dv) == 0);
+  }
+
+  if (m->top != 0) {   /* check top chunk */
+    do_check_top_chunk(m, m->top);
+    /*assert(m->topsize == chunksize(m->top)); redundant */
+    assert(m->topsize > 0);
+    assert(bin_find(m, m->top) == 0);
+  }
+
+  total = traverse_and_check(m);
+  assert(total <= m->footprint);
+  assert(m->footprint <= m->max_footprint);
+}
+#endif /* DEBUG */
+
+/* ----------------------------- statistics ------------------------------ */
+
+#if !NO_MALLINFO
+static struct mallinfo internal_mallinfo(mstate m) {
+  struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+  ensure_initialization();
+  if (!PREACTION(m)) {
+    check_malloc_state(m);
+    if (is_initialized(m)) {
+      size_t nfree = SIZE_T_ONE; /* top always free */
+      size_t mfree = m->topsize + TOP_FOOT_SIZE;
+      size_t sum = mfree;
+      msegmentptr s = &m->seg;
+      while (s != 0) {
+       mchunkptr q = align_as_chunk(s->base);
+       while (segment_holds(s, q) &&
+              q != m->top && q->head != FENCEPOST_HEAD) {
+         size_t sz = chunksize(q);
+         sum += sz;
+         if (!cinuse(q)) {
+           mfree += sz;
+           ++nfree;
+         }
+         q = next_chunk(q);
+       }
+       s = s->next;
+      }
+
+      nm.arena    = sum;
+      nm.ordblks  = nfree;
+      nm.hblkhd   = m->footprint - sum;
+      nm.usmblks  = m->max_footprint;
+      nm.uordblks = m->footprint - mfree;
+      nm.fordblks = mfree;
+      nm.keepcost = m->topsize;
+    }
+
+    POSTACTION(m);
+  }
+  return nm;
+}
+#endif /* !NO_MALLINFO */
+
+static void internal_malloc_stats(mstate m) {
+  ensure_initialization();
+  if (!PREACTION(m)) {
+    size_t maxfp = 0;
+    size_t fp = 0;
+    size_t used = 0;
+    check_malloc_state(m);
+    if (is_initialized(m)) {
+      msegmentptr s = &m->seg;
+      maxfp = m->max_footprint;
+      fp = m->footprint;
+      used = fp - (m->topsize + TOP_FOOT_SIZE);
+
+      while (s != 0) {
+       mchunkptr q = align_as_chunk(s->base);
+       while (segment_holds(s, q) &&
+              q != m->top && q->head != FENCEPOST_HEAD) {
+         if (!cinuse(q))
+           used -= chunksize(q);
+         q = next_chunk(q);
+       }
+       s = s->next;
+      }
+    }
+
+    fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp));
+    fprintf(stderr, "system bytes     = %10lu\n", (unsigned long)(fp));
+    fprintf(stderr, "in use bytes     = %10lu\n", (unsigned long)(used));
+
+    POSTACTION(m);
+  }
+}
+
+/* ----------------------- Operations on smallbins ----------------------- */
+
+/*
+  Various forms of linking and unlinking are defined as macros.  Even
+  the ones for trees, which are very long but have very short typical
+  paths.  This is ugly but reduces reliance on inlining support of
+  compilers.
+*/
+
+/* Link a free chunk into a smallbin  */
+#define insert_small_chunk(M, P, S) {\
+  bindex_t I  = small_index(S);\
+  mchunkptr B = smallbin_at(M, I);\
+  mchunkptr F = B;\
+  assert(S >= MIN_CHUNK_SIZE);\
+  if (!smallmap_is_marked(M, I))\
+    mark_smallmap(M, I);\
+  else if (RTCHECK(ok_address(M, B->fd)))\
+    F = B->fd;\
+  else {\
+    CORRUPTION_ERROR_ACTION(M);\
+  }\
+  B->fd = P;\
+  F->bk = P;\
+  P->fd = F;\
+  P->bk = B;\
+}
+
+/* Unlink a chunk from a smallbin  */
+#define unlink_small_chunk(M, P, S) {\
+  mchunkptr F = P->fd;\
+  mchunkptr B = P->bk;\
+  bindex_t I = small_index(S);\
+  assert(P != B);\
+  assert(P != F);\
+  assert(chunksize(P) == small_index2size(I));\
+  if (F == B)\
+    clear_smallmap(M, I);\
+  else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\
+                  (B == smallbin_at(M,I) || ok_address(M, B)))) {\
+    F->bk = B;\
+    B->fd = F;\
+  }\
+  else {\
+    CORRUPTION_ERROR_ACTION(M);\
+  }\
+}
+
+/* Unlink the first chunk from a smallbin */
+#define unlink_first_small_chunk(M, B, P, I) {\
+  mchunkptr F = P->fd;\
+  assert(P != B);\
+  assert(P != F);\
+  assert(chunksize(P) == small_index2size(I));\
+  if (B == F)\
+    clear_smallmap(M, I);\
+  else if (RTCHECK(ok_address(M, F))) {\
+    B->fd = F;\
+    F->bk = B;\
+  }\
+  else {\
+    CORRUPTION_ERROR_ACTION(M);\
+  }\
+}
+
+
+
+/* Replace dv node, binning the old one */
+/* Used only when dvsize known to be small */
+#define replace_dv(M, P, S) {\
+  size_t DVS = M->dvsize;\
+  if (DVS != 0) {\
+    mchunkptr DV = M->dv;\
+    assert(is_small(DVS));\
+    insert_small_chunk(M, DV, DVS);\
+  }\
+  M->dvsize = S;\
+  M->dv = P;\
+}
+
+/* ------------------------- Operations on trees ------------------------- */
+
+/* Insert chunk into tree */
+#define insert_large_chunk(M, X, S) {\
+  tbinptr* H;\
+  bindex_t I;\
+  compute_tree_index(S, I);\
+  H = treebin_at(M, I);\
+  X->index = I;\
+  X->child[0] = X->child[1] = 0;\
+  if (!treemap_is_marked(M, I)) {\
+    mark_treemap(M, I);\
+    *H = X;\
+    X->parent = (tchunkptr)H;\
+    X->fd = X->bk = X;\
+  }\
+  else {\
+    tchunkptr T = *H;\
+    size_t K = S << leftshift_for_tree_index(I);\
+    for (;;) {\
+      if (chunksize(T) != S) {\
+       tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\
+       K <<= 1;\
+       if (*C != 0)\
+         T = *C;\
+       else if (RTCHECK(ok_address(M, C))) {\
+         *C = X;\
+         X->parent = T;\
+         X->fd = X->bk = X;\
+         break;\
+       }\
+       else {\
+         CORRUPTION_ERROR_ACTION(M);\
+         break;\
+       }\
+      }\
+      else {\
+       tchunkptr F = T->fd;\
+       if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\
+         T->fd = F->bk = X;\
+         X->fd = F;\
+         X->bk = T;\
+         X->parent = 0;\
+         break;\
+       }\
+       else {\
+         CORRUPTION_ERROR_ACTION(M);\
+         break;\
+       }\
+      }\
+    }\
+  }\
+}
+
+/*
+  Unlink steps:
+
+  1. If x is a chained node, unlink it from its same-sized fd/bk links
+     and choose its bk node as its replacement.
+  2. If x was the last node of its size, but not a leaf node, it must
+     be replaced with a leaf node (not merely one with an open left or
+     right), to make sure that lefts and rights of descendents
+     correspond properly to bit masks.  We use the rightmost descendent
+     of x.  We could use any other leaf, but this is easy to locate and
+     tends to counteract removal of leftmosts elsewhere, and so keeps
+     paths shorter than minimally guaranteed.  This doesn't loop much
+     because on average a node in a tree is near the bottom.
+  3. If x is the base of a chain (i.e., has parent links) relink
+     x's parent and children to x's replacement (or null if none).
+*/
+
+#define unlink_large_chunk(M, X) {\
+  tchunkptr XP = X->parent;\
+  tchunkptr R;\
+  if (X->bk != X) {\
+    tchunkptr F = X->fd;\
+    R = X->bk;\
+    if (RTCHECK(ok_address(M, F))) {\
+      F->bk = R;\
+      R->fd = F;\
+    }\
+    else {\
+      CORRUPTION_ERROR_ACTION(M);\
+    }\
+  }\
+  else {\
+    tchunkptr* RP;\
+    if (((R = *(RP = &(X->child[1]))) != 0) ||\
+       ((R = *(RP = &(X->child[0]))) != 0)) {\
+      tchunkptr* CP;\
+      while ((*(CP = &(R->child[1])) != 0) ||\
+            (*(CP = &(R->child[0])) != 0)) {\
+       R = *(RP = CP);\
+      }\
+      if (RTCHECK(ok_address(M, RP)))\
+       *RP = 0;\
+      else {\
+       CORRUPTION_ERROR_ACTION(M);\
+      }\
+    }\
+  }\
+  if (XP != 0) {\
+    tbinptr* H = treebin_at(M, X->index);\
+    if (X == *H) {\
+      if ((*H = R) == 0) \
+       clear_treemap(M, X->index);\
+    }\
+    else if (RTCHECK(ok_address(M, XP))) {\
+      if (XP->child[0] == X) \
+       XP->child[0] = R;\
+      else \
+       XP->child[1] = R;\
+    }\
+    else\
+      CORRUPTION_ERROR_ACTION(M);\
+    if (R != 0) {\
+      if (RTCHECK(ok_address(M, R))) {\
+       tchunkptr C0, C1;\
+       R->parent = XP;\
+       if ((C0 = X->child[0]) != 0) {\
+         if (RTCHECK(ok_address(M, C0))) {\
+           R->child[0] = C0;\
+           C0->parent = R;\
+         }\
+         else\
+           CORRUPTION_ERROR_ACTION(M);\
+       }\
+       if ((C1 = X->child[1]) != 0) {\
+         if (RTCHECK(ok_address(M, C1))) {\
+           R->child[1] = C1;\
+           C1->parent = R;\
+         }\
+         else\
+           CORRUPTION_ERROR_ACTION(M);\
+       }\
+      }\
+      else\
+       CORRUPTION_ERROR_ACTION(M);\
+    }\
+  }\
+}
+
+/* Relays to large vs small bin operations */
+
+#define insert_chunk(M, P, S)\
+  if (is_small(S)) insert_small_chunk(M, P, S)\
+  else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); }
+
+#define unlink_chunk(M, P, S)\
+  if (is_small(S)) unlink_small_chunk(M, P, S)\
+  else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); }
+
+
+/* Relays to internal calls to malloc/free from realloc, memalign etc */
+
+#if ONLY_MSPACES
+#define internal_malloc(m, b) mspace_malloc(m, b)
+#define internal_free(m, mem) mspace_free(m,mem);
+#else /* ONLY_MSPACES */
+#if MSPACES
+#define internal_malloc(m, b)\
+   (m == gm)? dlmalloc(b) : mspace_malloc(m, b)
+#define internal_free(m, mem)\
+   if (m == gm) dlfree(mem); else mspace_free(m,mem);
+#else /* MSPACES */
+#define internal_malloc(m, b) dlmalloc(b)
+#define internal_free(m, mem) dlfree(mem)
+#endif /* MSPACES */
+#endif /* ONLY_MSPACES */
+
+/* -----------------------  Direct-mmapping chunks ----------------------- */
+
+/*
+  Directly mmapped chunks are set up with an offset to the start of
+  the mmapped region stored in the prev_foot field of the chunk. This
+  allows reconstruction of the required argument to MUNMAP when freed,
+  and also allows adjustment of the returned chunk to meet alignment
+  requirements (especially in memalign).  There is also enough space
+  allocated to hold a fake next chunk of size SIZE_T_SIZE to maintain
+  the PINUSE bit so frees can be checked.
+*/
+
+/* Malloc using mmap */
+static void* mmap_alloc(mstate m, size_t nb) {
+  size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+  if (mmsize > nb) {     /* Check for wrap around 0 */
+    char* mm = (char*)(CALL_DIRECT_MMAP(mmsize));
+    if (mm != CMFAIL) {
+      size_t offset = align_offset(chunk2mem(mm));
+      size_t psize = mmsize - offset - MMAP_FOOT_PAD;
+      mchunkptr p = (mchunkptr)(mm + offset);
+      p->prev_foot = offset | IS_MMAPPED_BIT;
+      (p)->head = (psize|CINUSE_BIT);
+      mark_inuse_foot(m, p, psize);
+      chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD;
+      chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0;
+
+      if (mm < m->least_addr)
+       m->least_addr = mm;
+      if ((m->footprint += mmsize) > m->max_footprint)
+       m->max_footprint = m->footprint;
+      assert(is_aligned(chunk2mem(p)));
+      check_mmapped_chunk(m, p);
+      return chunk2mem(p);
+    }
+  }
+  return 0;
+}
+
+/* Realloc using mmap */
+static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) {
+  size_t oldsize = chunksize(oldp);
+  if (is_small(nb)) /* Can't shrink mmap regions below small size */
+    return 0;
+  /* Keep old chunk if big enough but not too big */
+  if (oldsize >= nb + SIZE_T_SIZE &&
+      (oldsize - nb) <= (mparams.granularity << 1))
+    return oldp;
+  else {
+    size_t offset = oldp->prev_foot & ~IS_MMAPPED_BIT;
+    size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD;
+    size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+    char* cp = (char*)CALL_MREMAP((char*)oldp - offset,
+                                 oldmmsize, newmmsize, 1);
+    if (cp != CMFAIL) {
+      mchunkptr newp = (mchunkptr)(cp + offset);
+      size_t psize = newmmsize - offset - MMAP_FOOT_PAD;
+      newp->head = (psize|CINUSE_BIT);
+      mark_inuse_foot(m, newp, psize);
+      chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD;
+      chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0;
+
+      if (cp < m->least_addr)
+       m->least_addr = cp;
+      if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint)
+       m->max_footprint = m->footprint;
+      check_mmapped_chunk(m, newp);
+      return newp;
+    }
+  }
+  return 0;
+}
+
+/* -------------------------- mspace management -------------------------- */
+
+/* Initialize top chunk and its size */
+static void init_top(mstate m, mchunkptr p, size_t psize) {
+  /* Ensure alignment */
+  size_t offset = align_offset(chunk2mem(p));
+  p = (mchunkptr)((char*)p + offset);
+  psize -= offset;
+
+  m->top = p;
+  m->topsize = psize;
+  p->head = psize | PINUSE_BIT;
+  /* set size of fake trailing chunk holding overhead space only once */
+  chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE;
+  m->trim_check = mparams.trim_threshold; /* reset on each update */
+}
+
+/* Initialize bins for a new mstate that is otherwise zeroed out */
+static void init_bins(mstate m) {
+  /* Establish circular links for smallbins */
+  bindex_t i;
+  for (i = 0; i < NSMALLBINS; ++i) {
+    sbinptr bin = smallbin_at(m,i);
+    bin->fd = bin->bk = bin;
+  }
+}
+
+#if PROCEED_ON_ERROR
+
+/* default corruption action */
+static void reset_on_error(mstate m) {
+  int i;
+  ++malloc_corruption_error_count;
+  /* Reinitialize fields to forget about all memory */
+  m->smallbins = m->treebins = 0;
+  m->dvsize = m->topsize = 0;
+  m->seg.base = 0;
+  m->seg.size = 0;
+  m->seg.next = 0;
+  m->top = m->dv = 0;
+  for (i = 0; i < NTREEBINS; ++i)
+    *treebin_at(m, i) = 0;
+  init_bins(m);
+}
+#endif /* PROCEED_ON_ERROR */
+
+/* Allocate chunk and prepend remainder with chunk in successor base. */
+static void* prepend_alloc(mstate m, char* newbase, char* oldbase,
+                          size_t nb) {
+  mchunkptr p = align_as_chunk(newbase);
+  mchunkptr oldfirst = align_as_chunk(oldbase);
+  size_t psize = (char*)oldfirst - (char*)p;
+  mchunkptr q = chunk_plus_offset(p, nb);
+  size_t qsize = psize - nb;
+  set_size_and_pinuse_of_inuse_chunk(m, p, nb);
+
+  assert((char*)oldfirst > (char*)q);
+  assert(pinuse(oldfirst));
+  assert(qsize >= MIN_CHUNK_SIZE);
+
+  /* consolidate remainder with first chunk of old base */
+  if (oldfirst == m->top) {
+    size_t tsize = m->topsize += qsize;
+    m->top = q;
+    q->head = tsize | PINUSE_BIT;
+    check_top_chunk(m, q);
+  }
+  else if (oldfirst == m->dv) {
+    size_t dsize = m->dvsize += qsize;
+    m->dv = q;
+    set_size_and_pinuse_of_free_chunk(q, dsize);
+  }
+  else {
+    if (!cinuse(oldfirst)) {
+      size_t nsize = chunksize(oldfirst);
+      unlink_chunk(m, oldfirst, nsize);
+      oldfirst = chunk_plus_offset(oldfirst, nsize);
+      qsize += nsize;
+    }
+    set_free_with_pinuse(q, qsize, oldfirst);
+    insert_chunk(m, q, qsize);
+    check_free_chunk(m, q);
+  }
+
+  check_malloced_chunk(m, chunk2mem(p), nb);
+  return chunk2mem(p);
+}
+
+/* Add a segment to hold a new noncontiguous region */
+static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) {
+  /* Determine locations and sizes of segment, fenceposts, old top */
+  char* old_top = (char*)m->top;
+  msegmentptr oldsp = segment_holding(m, old_top);
+  char* old_end = oldsp->base + oldsp->size;
+  size_t ssize = pad_request(sizeof(struct malloc_segment));
+  char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+  size_t offset = align_offset(chunk2mem(rawsp));
+  char* asp = rawsp + offset;
+  char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp;
+  mchunkptr sp = (mchunkptr)csp;
+  msegmentptr ss = (msegmentptr)(chunk2mem(sp));
+  mchunkptr tnext = chunk_plus_offset(sp, ssize);
+  mchunkptr p = tnext;
+  int nfences = 0;
+
+  /* reset top to new space */
+  init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
+
+  /* Set up segment record */
+  assert(is_aligned(ss));
+  set_size_and_pinuse_of_inuse_chunk(m, sp, ssize);
+  *ss = m->seg; /* Push current record */
+  m->seg.base = tbase;
+  m->seg.size = tsize;
+  m->seg.sflags = mmapped;
+  m->seg.next = ss;
+
+  /* Insert trailing fenceposts */
+  for (;;) {
+    mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE);
+    p->head = FENCEPOST_HEAD;
+    ++nfences;
+    if ((char*)(&(nextp->head)) < old_end)
+      p = nextp;
+    else
+      break;
+  }
+  assert(nfences >= 2);
+
+  /* Insert the rest of old top into a bin as an ordinary free chunk */
+  if (csp != old_top) {
+    mchunkptr q = (mchunkptr)old_top;
+    size_t psize = csp - old_top;
+    mchunkptr tn = chunk_plus_offset(q, psize);
+    set_free_with_pinuse(q, psize, tn);
+    insert_chunk(m, q, psize);
+  }
+
+  check_top_chunk(m, m->top);
+}
+
+/* -------------------------- System allocation -------------------------- */
+
+/* Get memory from system using MORECORE or MMAP */
+static void* sys_alloc(mstate m, size_t nb) {
+  char* tbase = CMFAIL;
+  size_t tsize = 0;
+  flag_t mmap_flag = 0;
+
+  ensure_initialization();
+
+  /* Directly map large chunks */
+  if (use_mmap(m) && nb >= mparams.mmap_threshold) {
+    void* mem = mmap_alloc(m, nb);
+    if (mem != 0)
+      return mem;
+  }
+
+  /*
+    Try getting memory in any of three ways (in most-preferred to
+    least-preferred order):
+    1. A call to MORECORE that can normally contiguously extend memory.
+       (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or
+       or main space is mmapped or a previous contiguous call failed)
+    2. A call to MMAP new space (disabled if not HAVE_MMAP).
+       Note that under the default settings, if MORECORE is unable to
+       fulfill a request, and HAVE_MMAP is true, then mmap is
+       used as a noncontiguous system allocator. This is a useful backup
+       strategy for systems with holes in address spaces -- in this case
+       sbrk cannot contiguously expand the heap, but mmap may be able to
+       find space.
+    3. A call to MORECORE that cannot usually contiguously extend memory.
+       (disabled if not HAVE_MORECORE)
+
+   In all cases, we need to request enough bytes from system to ensure
+   we can malloc nb bytes upon success, so pad with enough space for
+   top_foot, plus alignment-pad to make sure we don't lose bytes if
+   not on boundary, and round this up to a granularity unit.
+  */
+
+  if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) {
+    char* br = CMFAIL;
+    msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top);
+    size_t asize = 0;
+    ACQUIRE_MALLOC_GLOBAL_LOCK();
+
+    if (ss == 0) {  /* First time through or recovery */
+      char* base = (char*)CALL_MORECORE(0);
+      if (base != CMFAIL) {
+       asize = granularity_align(nb + SYS_ALLOC_PADDING);
+       /* Adjust to end on a page boundary */
+       if (!is_page_aligned(base))
+         asize += (page_align((size_t)base) - (size_t)base);
+       /* Can't call MORECORE if size is negative when treated as signed */
+       if (asize < HALF_MAX_SIZE_T &&
+           (br = (char*)(CALL_MORECORE(asize))) == base) {
+         tbase = base;
+         tsize = asize;
+       }
+      }
+    }
+    else {
+      /* Subtract out existing available top space from MORECORE request. */
+      asize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING);
+      /* Use mem here only if it did continuously extend old space */
+      if (asize < HALF_MAX_SIZE_T &&
+         (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) {
+       tbase = br;
+       tsize = asize;
+      }
+    }
+
+    if (tbase == CMFAIL) {    /* Cope with partial failure */
+      if (br != CMFAIL) {    /* Try to use/extend the space we did get */
+       if (asize < HALF_MAX_SIZE_T &&
+           asize < nb + SYS_ALLOC_PADDING) {
+         size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - asize);
+         if (esize < HALF_MAX_SIZE_T) {
+           char* end = (char*)CALL_MORECORE(esize);
+           if (end != CMFAIL)
+             asize += esize;
+           else {            /* Can't use; try to release */
+             (void) CALL_MORECORE(-asize);
+             br = CMFAIL;
+           }
+         }
+       }
+      }
+      if (br != CMFAIL) {    /* Use the space we did get */
+       tbase = br;
+       tsize = asize;
+      }
+      else
+       disable_contiguous(m); /* Don't try contiguous path in the future */
+    }
+
+    RELEASE_MALLOC_GLOBAL_LOCK();
+  }
+
+  if (HAVE_MMAP && tbase == CMFAIL) {  /* Try MMAP */
+    size_t rsize = granularity_align(nb + SYS_ALLOC_PADDING);
+    if (rsize > nb) { /* Fail if wraps around zero */
+      char* mp = (char*)(CALL_MMAP(rsize));
+      if (mp != CMFAIL) {
+       tbase = mp;
+       tsize = rsize;
+       mmap_flag = IS_MMAPPED_BIT;
+      }
+    }
+  }
+
+  if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */
+    size_t asize = granularity_align(nb + SYS_ALLOC_PADDING);
+    if (asize < HALF_MAX_SIZE_T) {
+      char* br = CMFAIL;
+      char* end = CMFAIL;
+      ACQUIRE_MALLOC_GLOBAL_LOCK();
+      br = (char*)(CALL_MORECORE(asize));
+      end = (char*)(CALL_MORECORE(0));
+      RELEASE_MALLOC_GLOBAL_LOCK();
+      if (br != CMFAIL && end != CMFAIL && br < end) {
+       size_t ssize = end - br;
+       if (ssize > nb + TOP_FOOT_SIZE) {
+         tbase = br;
+         tsize = ssize;
+       }
+      }
+    }
+  }
+
+  if (tbase != CMFAIL) {
+
+    if ((m->footprint += tsize) > m->max_footprint)
+      m->max_footprint = m->footprint;
+
+    if (!is_initialized(m)) { /* first-time initialization */
+      m->seg.base = m->least_addr = tbase;
+      m->seg.size = tsize;
+      m->seg.sflags = mmap_flag;
+      m->magic = mparams.magic;
+      m->release_checks = MAX_RELEASE_CHECK_RATE;
+      init_bins(m);
+#if !ONLY_MSPACES
+      if (is_global(m))
+       init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
+      else
+#endif
+      {
+       /* Offset top by embedded malloc_state */
+       mchunkptr mn = next_chunk(mem2chunk(m));
+       init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE);
+      }
+    }
+
+    else {
+      /* Try to merge with an existing segment */
+      msegmentptr sp = &m->seg;
+      /* Only consider most recent segment if traversal suppressed */
+      while (sp != 0 && tbase != sp->base + sp->size)
+       sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next;
+      if (sp != 0 &&
+         !is_extern_segment(sp) &&
+         (sp->sflags & IS_MMAPPED_BIT) == mmap_flag &&
+         segment_holds(sp, m->top)) { /* append */
+       sp->size += tsize;
+       init_top(m, m->top, m->topsize + tsize);
+      }
+      else {
+       if (tbase < m->least_addr)
+         m->least_addr = tbase;
+       sp = &m->seg;
+       while (sp != 0 && sp->base != tbase + tsize)
+         sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next;
+       if (sp != 0 &&
+           !is_extern_segment(sp) &&
+           (sp->sflags & IS_MMAPPED_BIT) == mmap_flag) {
+         char* oldbase = sp->base;
+         sp->base = tbase;
+         sp->size += tsize;
+         return prepend_alloc(m, tbase, oldbase, nb);
+       }
+       else
+         add_segment(m, tbase, tsize, mmap_flag);
+      }
+    }
+
+    if (nb < m->topsize) { /* Allocate from new or extended top space */
+      size_t rsize = m->topsize -= nb;
+      mchunkptr p = m->top;
+      mchunkptr r = m->top = chunk_plus_offset(p, nb);
+      r->head = rsize | PINUSE_BIT;
+      set_size_and_pinuse_of_inuse_chunk(m, p, nb);
+      check_top_chunk(m, m->top);
+      check_malloced_chunk(m, chunk2mem(p), nb);
+      return chunk2mem(p);
+    }
+  }
+
+  MALLOC_FAILURE_ACTION;
+  return 0;
+}
+
+/* -----------------------  system deallocation -------------------------- */
+
+/* Unmap and unlink any mmapped segments that don't contain used chunks */
+static size_t release_unused_segments(mstate m) {
+  size_t released = 0;
+  int nsegs = 0;
+  msegmentptr pred = &m->seg;
+  msegmentptr sp = pred->next;
+  while (sp != 0) {
+    char* base = sp->base;
+    size_t size = sp->size;
+    msegmentptr next = sp->next;
+    ++nsegs;
+    if (is_mmapped_segment(sp) && !is_extern_segment(sp)) {
+      mchunkptr p = align_as_chunk(base);
+      size_t psize = chunksize(p);
+      /* Can unmap if first chunk holds entire segment and not pinned */
+      if (!cinuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) {
+       tchunkptr tp = (tchunkptr)p;
+       assert(segment_holds(sp, (char*)sp));
+       if (p == m->dv) {
+         m->dv = 0;
+         m->dvsize = 0;
+       }
+       else {
+         unlink_large_chunk(m, tp);
+       }
+       if (CALL_MUNMAP(base, size) == 0) {
+         released += size;
+         m->footprint -= size;
+         /* unlink obsoleted record */
+         sp = pred;
+         sp->next = next;
+       }
+       else { /* back out if cannot unmap */
+         insert_large_chunk(m, tp, psize);
+       }
+      }
+    }
+    if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */
+      break;
+    pred = sp;
+    sp = next;
+  }
+  /* Reset check counter */
+  m->release_checks = ((nsegs > MAX_RELEASE_CHECK_RATE)?
+                      nsegs : MAX_RELEASE_CHECK_RATE);
+  return released;
+}
+
+static int sys_trim(mstate m, size_t pad) {
+  size_t released = 0;
+  ensure_initialization();
+  if (pad < MAX_REQUEST && is_initialized(m)) {
+    pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */
+
+    if (m->topsize > pad) {
+      /* Shrink top space in granularity-size units, keeping at least one */
+      size_t unit = mparams.granularity;
+      size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit -
+                     SIZE_T_ONE) * unit;
+      msegmentptr sp = segment_holding(m, (char*)m->top);
+
+      if (!is_extern_segment(sp)) {
+       if (is_mmapped_segment(sp)) {
+         if (HAVE_MMAP &&
+             sp->size >= extra &&
+             !has_segment_link(m, sp)) { /* can't shrink if pinned */
+           size_t newsize = sp->size - extra;
+           /* Prefer mremap, fall back to munmap */
+           if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) ||
+               (CALL_MUNMAP(sp->base + newsize, extra) == 0)) {
+             released = extra;
+           }
+         }
+       }
+       else if (HAVE_MORECORE) {
+         if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */
+           extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit;
+         ACQUIRE_MALLOC_GLOBAL_LOCK();
+         {
+           /* Make sure end of memory is where we last set it. */
+           char* old_br = (char*)(CALL_MORECORE(0));
+           if (old_br == sp->base + sp->size) {
+             char* rel_br = (char*)(CALL_MORECORE(-extra));
+             char* new_br = (char*)(CALL_MORECORE(0));
+             if (rel_br != CMFAIL && new_br < old_br)
+               released = old_br - new_br;
+           }
+         }
+         RELEASE_MALLOC_GLOBAL_LOCK();
+       }
+      }
+
+      if (released != 0) {
+       sp->size -= released;
+       m->footprint -= released;
+       init_top(m, m->top, m->topsize - released);
+       check_top_chunk(m, m->top);
+      }
+    }
+
+    /* Unmap any unused mmapped segments */
+    if (HAVE_MMAP)
+      released += release_unused_segments(m);
+
+    /* On failure, disable autotrim to avoid repeated failed future calls */
+    if (released == 0 && m->topsize > m->trim_check)
+      m->trim_check = MAX_SIZE_T;
+  }
+
+  return (released != 0)? 1 : 0;
+}
+
+
+/* ---------------------------- malloc support --------------------------- */
+
+/* allocate a large request from the best fitting chunk in a treebin */
+static void* tmalloc_large(mstate m, size_t nb) {
+  tchunkptr v = 0;
+  size_t rsize = -nb; /* Unsigned negation */
+  tchunkptr t;
+  bindex_t idx;
+  compute_tree_index(nb, idx);
+  if ((t = *treebin_at(m, idx)) != 0) {
+    /* Traverse tree for this bin looking for node with size == nb */
+    size_t sizebits = nb << leftshift_for_tree_index(idx);
+    tchunkptr rst = 0;  /* The deepest untaken right subtree */
+    for (;;) {
+      tchunkptr rt;
+      size_t trem = chunksize(t) - nb;
+      if (trem < rsize) {
+       v = t;
+       if ((rsize = trem) == 0)
+         break;
+      }
+      rt = t->child[1];
+      t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
+      if (rt != 0 && rt != t)
+       rst = rt;
+      if (t == 0) {
+       t = rst; /* set t to least subtree holding sizes > nb */
+       break;
+      }
+      sizebits <<= 1;
+    }
+  }
+  if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */
+    binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap;
+    if (leftbits != 0) {
+      bindex_t i;
+      binmap_t leastbit = least_bit(leftbits);
+      compute_bit2idx(leastbit, i);
+      t = *treebin_at(m, i);
+    }
+  }
+
+  while (t != 0) { /* find smallest of tree or subtree */
+    size_t trem = chunksize(t) - nb;
+    if (trem < rsize) {
+      rsize = trem;
+      v = t;
+    }
+    t = leftmost_child(t);
+  }
+
+  /*  If dv is a better fit, return 0 so malloc will use it */
+  if (v != 0 && rsize < (size_t)(m->dvsize - nb)) {
+    if (RTCHECK(ok_address(m, v))) { /* split */
+      mchunkptr r = chunk_plus_offset(v, nb);
+      assert(chunksize(v) == rsize + nb);
+      if (RTCHECK(ok_next(v, r))) {
+       unlink_large_chunk(m, v);
+       if (rsize < MIN_CHUNK_SIZE)
+         set_inuse_and_pinuse(m, v, (rsize + nb));
+       else {
+         set_size_and_pinuse_of_inuse_chunk(m, v, nb);
+         set_size_and_pinuse_of_free_chunk(r, rsize);
+         insert_chunk(m, r, rsize);
+       }
+       return chunk2mem(v);
+      }
+    }
+    CORRUPTION_ERROR_ACTION(m);
+  }
+  return 0;
+}
+
+/* allocate a small request from the best fitting chunk in a treebin */
+static void* tmalloc_small(mstate m, size_t nb) {
+  tchunkptr t, v;
+  size_t rsize;
+  bindex_t i;
+  binmap_t leastbit = least_bit(m->treemap);
+  compute_bit2idx(leastbit, i);
+  v = t = *treebin_at(m, i);
+  rsize = chunksize(t) - nb;
+
+  while ((t = leftmost_child(t)) != 0) {
+    size_t trem = chunksize(t) - nb;
+    if (trem < rsize) {
+      rsize = trem;
+      v = t;
+    }
+  }
+
+  if (RTCHECK(ok_address(m, v))) {
+    mchunkptr r = chunk_plus_offset(v, nb);
+    assert(chunksize(v) == rsize + nb);
+    if (RTCHECK(ok_next(v, r))) {
+      unlink_large_chunk(m, v);
+      if (rsize < MIN_CHUNK_SIZE)
+       set_inuse_and_pinuse(m, v, (rsize + nb));
+      else {
+       set_size_and_pinuse_of_inuse_chunk(m, v, nb);
+       set_size_and_pinuse_of_free_chunk(r, rsize);
+       replace_dv(m, r, rsize);
+      }
+      return chunk2mem(v);
+    }
+  }
+
+  CORRUPTION_ERROR_ACTION(m);
+  return 0;
+}
+
+/* --------------------------- realloc support --------------------------- */
+
+static void* internal_realloc(mstate m, void* oldmem, size_t bytes) {
+  if (bytes >= MAX_REQUEST) {
+    MALLOC_FAILURE_ACTION;
+    return 0;
+  }
+  if (!PREACTION(m)) {
+    mchunkptr oldp = mem2chunk(oldmem);
+    size_t oldsize = chunksize(oldp);
+    mchunkptr next = chunk_plus_offset(oldp, oldsize);
+    mchunkptr newp = 0;
+    void* extra = 0;
+
+    /* Try to either shrink or extend into top. Else malloc-copy-free */
+
+    if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) &&
+               ok_next(oldp, next) && ok_pinuse(next))) {
+      size_t nb = request2size(bytes);
+      if (is_mmapped(oldp))
+       newp = mmap_resize(m, oldp, nb);
+      else if (oldsize >= nb) { /* already big enough */
+       size_t rsize = oldsize - nb;
+       newp = oldp;
+       if (rsize >= MIN_CHUNK_SIZE) {
+         mchunkptr remainder = chunk_plus_offset(newp, nb);
+         set_inuse(m, newp, nb);
+         set_inuse(m, remainder, rsize);
+         extra = chunk2mem(remainder);
+       }
+      }
+      else if (next == m->top && oldsize + m->topsize > nb) {
+       /* Expand into top */
+       size_t newsize = oldsize + m->topsize;
+       size_t newtopsize = newsize - nb;
+       mchunkptr newtop = chunk_plus_offset(oldp, nb);
+       set_inuse(m, oldp, nb);
+       newtop->head = newtopsize |PINUSE_BIT;
+       m->top = newtop;
+       m->topsize = newtopsize;
+       newp = oldp;
+      }
+    }
+    else {
+      USAGE_ERROR_ACTION(m, oldmem);
+      POSTACTION(m);
+      return 0;
+    }
+
+    POSTACTION(m);
+
+    if (newp != 0) {
+      if (extra != 0) {
+       internal_free(m, extra);
+      }
+      check_inuse_chunk(m, newp);
+      return chunk2mem(newp);
+    }
+    else {
+      void* newmem = internal_malloc(m, bytes);
+      if (newmem != 0) {
+       size_t oc = oldsize - overhead_for(oldp);
+       memcpy(newmem, oldmem, (oc < bytes)? oc : bytes);
+       internal_free(m, oldmem);
+      }
+      return newmem;
+    }
+  }
+  return 0;
+}
+
+/* --------------------------- memalign support -------------------------- */
+
+static void* internal_memalign(mstate m, size_t alignment, size_t bytes) {
+  if (alignment <= MALLOC_ALIGNMENT)    /* Can just use malloc */
+    return internal_malloc(m, bytes);
+  if (alignment <  MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */
+    alignment = MIN_CHUNK_SIZE;
+  if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */
+    size_t a = MALLOC_ALIGNMENT << 1;
+    while (a < alignment) a <<= 1;
+    alignment = a;
+  }
+
+  if (bytes >= MAX_REQUEST - alignment) {
+    if (m != 0)  { /* Test isn't needed but avoids compiler warning */
+      MALLOC_FAILURE_ACTION;
+    }
+  }
+  else {
+    size_t nb = request2size(bytes);
+    size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD;
+    char* mem = (char*)internal_malloc(m, req);
+    if (mem != 0) {
+      void* leader = 0;
+      void* trailer = 0;
+      mchunkptr p = mem2chunk(mem);
+
+      if (PREACTION(m)) return 0;
+      if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */
+       /*
+         Find an aligned spot inside chunk.  Since we need to give
+         back leading space in a chunk of at least MIN_CHUNK_SIZE, if
+         the first calculation places us at a spot with less than
+         MIN_CHUNK_SIZE leader, we can move to the next aligned spot.
+         We've allocated enough total room so that this is always
+         possible.
+       */
+       char* br = (char*)mem2chunk((size_t)(((size_t)(mem +
+                                                      alignment -
+                                                      SIZE_T_ONE)) &
+                                            -alignment));
+       char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)?
+         br : br+alignment;
+       mchunkptr newp = (mchunkptr)pos;
+       size_t leadsize = pos - (char*)(p);
+       size_t newsize = chunksize(p) - leadsize;
+
+       if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */
+         newp->prev_foot = p->prev_foot + leadsize;
+         newp->head = (newsize|CINUSE_BIT);
+       }
+       else { /* Otherwise, give back leader, use the rest */
+         set_inuse(m, newp, newsize);
+         set_inuse(m, p, leadsize);
+         leader = chunk2mem(p);
+       }
+       p = newp;
+      }
+
+      /* Give back spare room at the end */
+      if (!is_mmapped(p)) {
+       size_t size = chunksize(p);
+       if (size > nb + MIN_CHUNK_SIZE) {
+         size_t remainder_size = size - nb;
+         mchunkptr remainder = chunk_plus_offset(p, nb);
+         set_inuse(m, p, nb);
+         set_inuse(m, remainder, remainder_size);
+         trailer = chunk2mem(remainder);
+       }
+      }
+
+      assert (chunksize(p) >= nb);
+      assert((((size_t)(chunk2mem(p))) % alignment) == 0);
+      check_inuse_chunk(m, p);
+      POSTACTION(m);
+      if (leader != 0) {
+       internal_free(m, leader);
+      }
+      if (trailer != 0) {
+       internal_free(m, trailer);
+      }
+      return chunk2mem(p);
+    }
+  }
+  return 0;
+}
+
+/* ------------------------ comalloc/coalloc support --------------------- */
+
+static void** ialloc(mstate m,
+                    size_t n_elements,
+                    size_t* sizes,
+                    int opts,
+                    void* chunks[]) {
+  /*
+    This provides common support for independent_X routines, handling
+    all of the combinations that can result.
+
+    The opts arg has:
+    bit 0 set if all elements are same size (using sizes[0])
+    bit 1 set if elements should be zeroed
+  */
+
+  size_t    element_size;   /* chunksize of each element, if all same */
+  size_t    contents_size;  /* total size of elements */
+  size_t    array_size;     /* request size of pointer array */
+  void*     mem;            /* malloced aggregate space */
+  mchunkptr p;              /* corresponding chunk */
+  size_t    remainder_size; /* remaining bytes while splitting */
+  void**    marray;         /* either "chunks" or malloced ptr array */
+  mchunkptr array_chunk;    /* chunk for malloced ptr array */
+  flag_t    was_enabled;    /* to disable mmap */
+  size_t    size;
+  size_t    i;
+
+  ensure_initialization();
+  /* compute array length, if needed */
+  if (chunks != 0) {
+    if (n_elements == 0)
+      return chunks; /* nothing to do */
+    marray = chunks;
+    array_size = 0;
+  }
+  else {
+    /* if empty req, must still return chunk representing empty array */
+    if (n_elements == 0)
+      return (void**)internal_malloc(m, 0);
+    marray = 0;
+    array_size = request2size(n_elements * (sizeof(void*)));
+  }
+
+  /* compute total element size */
+  if (opts & 0x1) { /* all-same-size */
+    element_size = request2size(*sizes);
+    contents_size = n_elements * element_size;
+  }
+  else { /* add up all the sizes */
+    element_size = 0;
+    contents_size = 0;
+    for (i = 0; i != n_elements; ++i)
+      contents_size += request2size(sizes[i]);
+  }
+
+  size = contents_size + array_size;
+
+  /*
+     Allocate the aggregate chunk.  First disable direct-mmapping so
+     malloc won't use it, since we would not be able to later
+     free/realloc space internal to a segregated mmap region.
+  */
+  was_enabled = use_mmap(m);
+  disable_mmap(m);
+  mem = internal_malloc(m, size - CHUNK_OVERHEAD);
+  if (was_enabled)
+    enable_mmap(m);
+  if (mem == 0)
+    return 0;
+
+  if (PREACTION(m)) return 0;
+  p = mem2chunk(mem);
+  remainder_size = chunksize(p);
+
+  assert(!is_mmapped(p));
+
+  if (opts & 0x2) {       /* optionally clear the elements */
+    memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size);
+  }
+
+  /* If not provided, allocate the pointer array as final part of chunk */
+  if (marray == 0) {
+    size_t  array_chunk_size;
+    array_chunk = chunk_plus_offset(p, contents_size);
+    array_chunk_size = remainder_size - contents_size;
+    marray = (void**) (chunk2mem(array_chunk));
+    set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size);
+    remainder_size = contents_size;
+  }
+
+  /* split out elements */
+  for (i = 0; ; ++i) {
+    marray[i] = chunk2mem(p);
+    if (i != n_elements-1) {
+      if (element_size != 0)
+       size = element_size;
+      else
+       size = request2size(sizes[i]);
+      remainder_size -= size;
+      set_size_and_pinuse_of_inuse_chunk(m, p, size);
+      p = chunk_plus_offset(p, size);
+    }
+    else { /* the final element absorbs any overallocation slop */
+      set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size);
+      break;
+    }
+  }
+
+#if DEBUG
+  if (marray != chunks) {
+    /* final element must have exactly exhausted chunk */
+    if (element_size != 0) {
+      assert(remainder_size == element_size);
+    }
+    else {
+      assert(remainder_size == request2size(sizes[i]));
+    }
+    check_inuse_chunk(m, mem2chunk(marray));
+  }
+  for (i = 0; i != n_elements; ++i)
+    check_inuse_chunk(m, mem2chunk(marray[i]));
+
+#endif /* DEBUG */
+
+  POSTACTION(m);
+  return marray;
+}
+
+
+/* -------------------------- public routines ---------------------------- */
+
+#if !ONLY_MSPACES
+
+void* dlmalloc(size_t bytes) {
+  /*
+     Basic algorithm:
+     If a small request (< 256 bytes minus per-chunk overhead):
+       1. If one exists, use a remainderless chunk in associated smallbin.
+         (Remainderless means that there are too few excess bytes to
+         represent as a chunk.)
+       2. If it is big enough, use the dv chunk, which is normally the
+         chunk adjacent to the one used for the most recent small request.
+       3. If one exists, split the smallest available chunk in a bin,
+         saving remainder in dv.
+       4. If it is big enough, use the top chunk.
+       5. If available, get memory from system and use it
+     Otherwise, for a large request:
+       1. Find the smallest available binned chunk that fits, and use it
+         if it is better fitting than dv chunk, splitting if necessary.
+       2. If better fitting than any binned chunk, use the dv chunk.
+       3. If it is big enough, use the top chunk.
+       4. If request size >= mmap threshold, try to directly mmap this chunk.
+       5. If available, get memory from system and use it
+
+     The ugly goto's here ensure that postaction occurs along all paths.
+  */
+
+#if USE_LOCKS
+  ensure_initialization(); /* initialize in sys_alloc if not using locks */
+#endif
+
+  if (!PREACTION(gm)) {
+    void* mem;
+    size_t nb;
+    if (bytes <= MAX_SMALL_REQUEST) {
+      bindex_t idx;
+      binmap_t smallbits;
+      nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
+      idx = small_index(nb);
+      smallbits = gm->smallmap >> idx;
+
+      if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
+       mchunkptr b, p;
+       idx += ~smallbits & 1;       /* Uses next bin if idx empty */
+       b = smallbin_at(gm, idx);
+       p = b->fd;
+       assert(chunksize(p) == small_index2size(idx));
+       unlink_first_small_chunk(gm, b, p, idx);
+       set_inuse_and_pinuse(gm, p, small_index2size(idx));
+       mem = chunk2mem(p);
+       check_malloced_chunk(gm, mem, nb);
+       goto postaction;
+      }
+
+      else if (nb > gm->dvsize) {
+       if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
+         mchunkptr b, p, r;
+         size_t rsize;
+         bindex_t i;
+         binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
+         binmap_t leastbit = least_bit(leftbits);
+         compute_bit2idx(leastbit, i);
+         b = smallbin_at(gm, i);
+         p = b->fd;
+         assert(chunksize(p) == small_index2size(i));
+         unlink_first_small_chunk(gm, b, p, i);
+         rsize = small_index2size(i) - nb;
+         /* Fit here cannot be remainderless if 4byte sizes */
+         if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
+           set_inuse_and_pinuse(gm, p, small_index2size(i));
+         else {
+           set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+           r = chunk_plus_offset(p, nb);
+           set_size_and_pinuse_of_free_chunk(r, rsize);
+           replace_dv(gm, r, rsize);
+         }
+         mem = chunk2mem(p);
+         check_malloced_chunk(gm, mem, nb);
+         goto postaction;
+       }
+
+       else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) {
+         check_malloced_chunk(gm, mem, nb);
+         goto postaction;
+       }
+      }
+    }
+    else if (bytes >= MAX_REQUEST)
+      nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
+    else {
+      nb = pad_request(bytes);
+      if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) {
+       check_malloced_chunk(gm, mem, nb);
+       goto postaction;
+      }
+    }
+
+    if (nb <= gm->dvsize) {
+      size_t rsize = gm->dvsize - nb;
+      mchunkptr p = gm->dv;
+      if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
+       mchunkptr r = gm->dv = chunk_plus_offset(p, nb);
+       gm->dvsize = rsize;
+       set_size_and_pinuse_of_free_chunk(r, rsize);
+       set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+      }
+      else { /* exhaust dv */
+       size_t dvs = gm->dvsize;
+       gm->dvsize = 0;
+       gm->dv = 0;
+       set_inuse_and_pinuse(gm, p, dvs);
+      }
+      mem = chunk2mem(p);
+      check_malloced_chunk(gm, mem, nb);
+      goto postaction;
+    }
+
+    else if (nb < gm->topsize) { /* Split top */
+      size_t rsize = gm->topsize -= nb;
+      mchunkptr p = gm->top;
+      mchunkptr r = gm->top = chunk_plus_offset(p, nb);
+      r->head = rsize | PINUSE_BIT;
+      set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+      mem = chunk2mem(p);
+      check_top_chunk(gm, gm->top);
+      check_malloced_chunk(gm, mem, nb);
+      goto postaction;
+    }
+
+    mem = sys_alloc(gm, nb);
+
+  postaction:
+    POSTACTION(gm);
+    return mem;
+  }
+
+  return 0;
+}
+
+void dlfree(void* mem) {
+  /*
+     Consolidate freed chunks with preceeding or succeeding bordering
+     free chunks, if they exist, and then place in a bin.  Intermixed
+     with special cases for top, dv, mmapped chunks, and usage errors.
+  */
+
+  if (mem != 0) {
+    mchunkptr p  = mem2chunk(mem);
+#if FOOTERS
+    mstate fm = get_mstate_for(p);
+    if (!ok_magic(fm)) {
+      USAGE_ERROR_ACTION(fm, p);
+      return;
+    }
+#else /* FOOTERS */
+#define fm gm
+#endif /* FOOTERS */
+    if (!PREACTION(fm)) {
+      check_inuse_chunk(fm, p);
+      if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) {
+       size_t psize = chunksize(p);
+       mchunkptr next = chunk_plus_offset(p, psize);
+       if (!pinuse(p)) {
+         size_t prevsize = p->prev_foot;
+         if ((prevsize & IS_MMAPPED_BIT) != 0) {
+           prevsize &= ~IS_MMAPPED_BIT;
+           psize += prevsize + MMAP_FOOT_PAD;
+           if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+             fm->footprint -= psize;
+           goto postaction;
+         }
+         else {
+           mchunkptr prev = chunk_minus_offset(p, prevsize);
+           psize += prevsize;
+           p = prev;
+           if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
+             if (p != fm->dv) {
+               unlink_chunk(fm, p, prevsize);
+             }
+             else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+               fm->dvsize = psize;
+               set_free_with_pinuse(p, psize, next);
+               goto postaction;
+             }
+           }
+           else
+             goto erroraction;
+         }
+       }
+
+       if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
+         if (!cinuse(next)) {  /* consolidate forward */
+           if (next == fm->top) {
+             size_t tsize = fm->topsize += psize;
+             fm->top = p;
+             p->head = tsize | PINUSE_BIT;
+             if (p == fm->dv) {
+               fm->dv = 0;
+               fm->dvsize = 0;
+             }
+             if (should_trim(fm, tsize))
+               sys_trim(fm, 0);
+             goto postaction;
+           }
+           else if (next == fm->dv) {
+             size_t dsize = fm->dvsize += psize;
+             fm->dv = p;
+             set_size_and_pinuse_of_free_chunk(p, dsize);
+             goto postaction;
+           }
+           else {
+             size_t nsize = chunksize(next);
+             psize += nsize;
+             unlink_chunk(fm, next, nsize);
+             set_size_and_pinuse_of_free_chunk(p, psize);
+             if (p == fm->dv) {
+               fm->dvsize = psize;
+               goto postaction;
+             }
+           }
+         }
+         else
+           set_free_with_pinuse(p, psize, next);
+
+         if (is_small(psize)) {
+           insert_small_chunk(fm, p, psize);
+           check_free_chunk(fm, p);
+         }
+         else {
+           tchunkptr tp = (tchunkptr)p;
+           insert_large_chunk(fm, tp, psize);
+           check_free_chunk(fm, p);
+           if (--fm->release_checks == 0)
+             release_unused_segments(fm);
+         }
+         goto postaction;
+       }
+      }
+    erroraction:
+      USAGE_ERROR_ACTION(fm, p);
+    postaction:
+      POSTACTION(fm);
+    }
+  }
+#if !FOOTERS
+#undef fm
+#endif /* FOOTERS */
+}
+
+void* dlcalloc(size_t n_elements, size_t elem_size) {
+  void* mem;
+  size_t req = 0;
+  if (n_elements != 0) {
+    req = n_elements * elem_size;
+    if (((n_elements | elem_size) & ~(size_t)0xffff) &&
+       (req / n_elements != elem_size))
+      req = MAX_SIZE_T; /* force downstream failure on overflow */
+  }
+  mem = dlmalloc(req);
+  if (mem != 0 && calloc_must_clear(mem2chunk(mem)))
+    memset(mem, 0, req);
+  return mem;
+}
+
+void* dlrealloc(void* oldmem, size_t bytes) {
+  if (oldmem == 0)
+    return dlmalloc(bytes);
+#ifdef REALLOC_ZERO_BYTES_FREES
+  if (bytes == 0) {
+    dlfree(oldmem);
+    return 0;
+  }
+#endif /* REALLOC_ZERO_BYTES_FREES */
+  else {
+#if ! FOOTERS
+    mstate m = gm;
+#else /* FOOTERS */
+    mstate m = get_mstate_for(mem2chunk(oldmem));
+    if (!ok_magic(m)) {
+      USAGE_ERROR_ACTION(m, oldmem);
+      return 0;
+    }
+#endif /* FOOTERS */
+    return internal_realloc(m, oldmem, bytes);
+  }
+}
+
+void* dlmemalign(size_t alignment, size_t bytes) {
+  return internal_memalign(gm, alignment, bytes);
+}
+
+void** dlindependent_calloc(size_t n_elements, size_t elem_size,
+                                void* chunks[]) {
+  size_t sz = elem_size; /* serves as 1-element array */
+  return ialloc(gm, n_elements, &sz, 3, chunks);
+}
+
+void** dlindependent_comalloc(size_t n_elements, size_t sizes[],
+                                  void* chunks[]) {
+  return ialloc(gm, n_elements, sizes, 0, chunks);
+}
+
+void* dlvalloc(size_t bytes) {
+  size_t pagesz;
+  ensure_initialization();
+  pagesz = mparams.page_size;
+  return dlmemalign(pagesz, bytes);
+}
+
+void* dlpvalloc(size_t bytes) {
+  size_t pagesz;
+  ensure_initialization();
+  pagesz = mparams.page_size;
+  return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE));
+}
+
+int dlmalloc_trim(size_t pad) {
+  ensure_initialization();
+  int result = 0;
+  if (!PREACTION(gm)) {
+    result = sys_trim(gm, pad);
+    POSTACTION(gm);
+  }
+  return result;
+}
+
+size_t dlmalloc_footprint(void) {
+  return gm->footprint;
+}
+
+size_t dlmalloc_max_footprint(void) {
+  return gm->max_footprint;
+}
+
+#if !NO_MALLINFO
+struct mallinfo dlmallinfo(void) {
+  return internal_mallinfo(gm);
+}
+#endif /* NO_MALLINFO */
+
+void dlmalloc_stats() {
+  internal_malloc_stats(gm);
+}
+
+int dlmallopt(int param_number, int value) {
+  return change_mparam(param_number, value);
+}
+
+#endif /* !ONLY_MSPACES */
+
+size_t dlmalloc_usable_size(void* mem) {
+  if (mem != 0) {
+    mchunkptr p = mem2chunk(mem);
+    if (cinuse(p))
+      return chunksize(p) - overhead_for(p);
+  }
+  return 0;
+}
+
+/* ----------------------------- user mspaces ---------------------------- */
+
+#if MSPACES
+
+static mstate init_user_mstate(char* tbase, size_t tsize) {
+  size_t msize = pad_request(sizeof(struct malloc_state));
+  mchunkptr mn;
+  mchunkptr msp = align_as_chunk(tbase);
+  mstate m = (mstate)(chunk2mem(msp));
+  memset(m, 0, msize);
+  INITIAL_LOCK(&m->mutex);
+  msp->head = (msize|PINUSE_BIT|CINUSE_BIT);
+  m->seg.base = m->least_addr = tbase;
+  m->seg.size = m->footprint = m->max_footprint = tsize;
+  m->magic = mparams.magic;
+  m->release_checks = MAX_RELEASE_CHECK_RATE;
+  m->mflags = mparams.default_mflags;
+  m->extp = 0;
+  m->exts = 0;
+  disable_contiguous(m);
+  init_bins(m);
+  mn = next_chunk(mem2chunk(m));
+  init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE);
+  check_top_chunk(m, m->top);
+  return m;
+}
+
+mspace create_mspace(size_t capacity, int locked) {
+  mstate m = 0;
+  size_t msize;
+  ensure_initialization();
+  msize = pad_request(sizeof(struct malloc_state));
+  if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
+    size_t rs = ((capacity == 0)? mparams.granularity :
+                (capacity + TOP_FOOT_SIZE + msize));
+    size_t tsize = granularity_align(rs);
+    char* tbase = (char*)(CALL_MMAP(tsize));
+    if (tbase != CMFAIL) {
+      m = init_user_mstate(tbase, tsize);
+      m->seg.sflags = IS_MMAPPED_BIT;
+      set_lock(m, locked);
+    }
+  }
+  return (mspace)m;
+}
+
+mspace create_mspace_with_base(void* base, size_t capacity, int locked) {
+  mstate m = 0;
+  size_t msize;
+  ensure_initialization();
+  msize = pad_request(sizeof(struct malloc_state));
+  if (capacity > msize + TOP_FOOT_SIZE &&
+      capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
+    m = init_user_mstate((char*)base, capacity);
+    m->seg.sflags = EXTERN_BIT;
+    set_lock(m, locked);
+  }
+  return (mspace)m;
+}
+
+int mspace_mmap_large_chunks(mspace msp, int enable) {
+  int ret = 0;
+  mstate ms = (mstate)msp;
+  if (!PREACTION(ms)) {
+    if (use_mmap(ms))
+      ret = 1;
+    if (enable)
+      enable_mmap(ms);
+    else
+      disable_mmap(ms);
+    POSTACTION(ms);
+  }
+  return ret;
+}
+
+size_t destroy_mspace(mspace msp) {
+  size_t freed = 0;
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    msegmentptr sp = &ms->seg;
+    while (sp != 0) {
+      char* base = sp->base;
+      size_t size = sp->size;
+      flag_t flag = sp->sflags;
+      sp = sp->next;
+      if ((flag & IS_MMAPPED_BIT) && !(flag & EXTERN_BIT) &&
+         CALL_MUNMAP(base, size) == 0)
+       freed += size;
+    }
+  }
+  else {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+  return freed;
+}
+
+/*
+  mspace versions of routines are near-clones of the global
+  versions. This is not so nice but better than the alternatives.
+*/
+
+
+void* mspace_malloc(mspace msp, size_t bytes) {
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  if (!PREACTION(ms)) {
+    void* mem;
+    size_t nb;
+    if (bytes <= MAX_SMALL_REQUEST) {
+      bindex_t idx;
+      binmap_t smallbits;
+      nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
+      idx = small_index(nb);
+      smallbits = ms->smallmap >> idx;
+
+      if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
+       mchunkptr b, p;
+       idx += ~smallbits & 1;       /* Uses next bin if idx empty */
+       b = smallbin_at(ms, idx);
+       p = b->fd;
+       assert(chunksize(p) == small_index2size(idx));
+       unlink_first_small_chunk(ms, b, p, idx);
+       set_inuse_and_pinuse(ms, p, small_index2size(idx));
+       mem = chunk2mem(p);
+       check_malloced_chunk(ms, mem, nb);
+       goto postaction;
+      }
+
+      else if (nb > ms->dvsize) {
+       if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
+         mchunkptr b, p, r;
+         size_t rsize;
+         bindex_t i;
+         binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
+         binmap_t leastbit = least_bit(leftbits);
+         compute_bit2idx(leastbit, i);
+         b = smallbin_at(ms, i);
+         p = b->fd;
+         assert(chunksize(p) == small_index2size(i));
+         unlink_first_small_chunk(ms, b, p, i);
+         rsize = small_index2size(i) - nb;
+         /* Fit here cannot be remainderless if 4byte sizes */
+         if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
+           set_inuse_and_pinuse(ms, p, small_index2size(i));
+         else {
+           set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+           r = chunk_plus_offset(p, nb);
+           set_size_and_pinuse_of_free_chunk(r, rsize);
+           replace_dv(ms, r, rsize);
+         }
+         mem = chunk2mem(p);
+         check_malloced_chunk(ms, mem, nb);
+         goto postaction;
+       }
+
+       else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) {
+         check_malloced_chunk(ms, mem, nb);
+         goto postaction;
+       }
+      }
+    }
+    else if (bytes >= MAX_REQUEST)
+      nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
+    else {
+      nb = pad_request(bytes);
+      if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) {
+       check_malloced_chunk(ms, mem, nb);
+       goto postaction;
+      }
+    }
+
+    if (nb <= ms->dvsize) {
+      size_t rsize = ms->dvsize - nb;
+      mchunkptr p = ms->dv;
+      if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
+       mchunkptr r = ms->dv = chunk_plus_offset(p, nb);
+       ms->dvsize = rsize;
+       set_size_and_pinuse_of_free_chunk(r, rsize);
+       set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+      }
+      else { /* exhaust dv */
+       size_t dvs = ms->dvsize;
+       ms->dvsize = 0;
+       ms->dv = 0;
+       set_inuse_and_pinuse(ms, p, dvs);
+      }
+      mem = chunk2mem(p);
+      check_malloced_chunk(ms, mem, nb);
+      goto postaction;
+    }
+
+    else if (nb < ms->topsize) { /* Split top */
+      size_t rsize = ms->topsize -= nb;
+      mchunkptr p = ms->top;
+      mchunkptr r = ms->top = chunk_plus_offset(p, nb);
+      r->head = rsize | PINUSE_BIT;
+      set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+      mem = chunk2mem(p);
+      check_top_chunk(ms, ms->top);
+      check_malloced_chunk(ms, mem, nb);
+      goto postaction;
+    }
+
+    mem = sys_alloc(ms, nb);
+
+  postaction:
+    POSTACTION(ms);
+    return mem;
+  }
+
+  return 0;
+}
+
+void mspace_free(mspace msp, void* mem) {
+  if (mem != 0) {
+    mchunkptr p  = mem2chunk(mem);
+#if FOOTERS
+    mstate fm = get_mstate_for(p);
+#else /* FOOTERS */
+    mstate fm = (mstate)msp;
+#endif /* FOOTERS */
+    if (!ok_magic(fm)) {
+      USAGE_ERROR_ACTION(fm, p);
+      return;
+    }
+    if (!PREACTION(fm)) {
+      check_inuse_chunk(fm, p);
+      if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) {
+       size_t psize = chunksize(p);
+       mchunkptr next = chunk_plus_offset(p, psize);
+       if (!pinuse(p)) {
+         size_t prevsize = p->prev_foot;
+         if ((prevsize & IS_MMAPPED_BIT) != 0) {
+           prevsize &= ~IS_MMAPPED_BIT;
+           psize += prevsize + MMAP_FOOT_PAD;
+           if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+             fm->footprint -= psize;
+           goto postaction;
+         }
+         else {
+           mchunkptr prev = chunk_minus_offset(p, prevsize);
+           psize += prevsize;
+           p = prev;
+           if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
+             if (p != fm->dv) {
+               unlink_chunk(fm, p, prevsize);
+             }
+             else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+               fm->dvsize = psize;
+               set_free_with_pinuse(p, psize, next);
+               goto postaction;
+             }
+           }
+           else
+             goto erroraction;
+         }
+       }
+
+       if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
+         if (!cinuse(next)) {  /* consolidate forward */
+           if (next == fm->top) {
+             size_t tsize = fm->topsize += psize;
+             fm->top = p;
+             p->head = tsize | PINUSE_BIT;
+             if (p == fm->dv) {
+               fm->dv = 0;
+               fm->dvsize = 0;
+             }
+             if (should_trim(fm, tsize))
+               sys_trim(fm, 0);
+             goto postaction;
+           }
+           else if (next == fm->dv) {
+             size_t dsize = fm->dvsize += psize;
+             fm->dv = p;
+             set_size_and_pinuse_of_free_chunk(p, dsize);
+             goto postaction;
+           }
+           else {
+             size_t nsize = chunksize(next);
+             psize += nsize;
+             unlink_chunk(fm, next, nsize);
+             set_size_and_pinuse_of_free_chunk(p, psize);
+             if (p == fm->dv) {
+               fm->dvsize = psize;
+               goto postaction;
+             }
+           }
+         }
+         else
+           set_free_with_pinuse(p, psize, next);
+
+         if (is_small(psize)) {
+           insert_small_chunk(fm, p, psize);
+           check_free_chunk(fm, p);
+         }
+         else {
+           tchunkptr tp = (tchunkptr)p;
+           insert_large_chunk(fm, tp, psize);
+           check_free_chunk(fm, p);
+           if (--fm->release_checks == 0)
+             release_unused_segments(fm);
+         }
+         goto postaction;
+       }
+      }
+    erroraction:
+      USAGE_ERROR_ACTION(fm, p);
+    postaction:
+      POSTACTION(fm);
+    }
+  }
+}
+
+void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) {
+  void* mem;
+  size_t req = 0;
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  if (n_elements != 0) {
+    req = n_elements * elem_size;
+    if (((n_elements | elem_size) & ~(size_t)0xffff) &&
+       (req / n_elements != elem_size))
+      req = MAX_SIZE_T; /* force downstream failure on overflow */
+  }
+  mem = internal_malloc(ms, req);
+  if (mem != 0 && calloc_must_clear(mem2chunk(mem)))
+    memset(mem, 0, req);
+  return mem;
+}
+
+void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) {
+  if (oldmem == 0)
+    return mspace_malloc(msp, bytes);
+#ifdef REALLOC_ZERO_BYTES_FREES
+  if (bytes == 0) {
+    mspace_free(msp, oldmem);
+    return 0;
+  }
+#endif /* REALLOC_ZERO_BYTES_FREES */
+  else {
+#if FOOTERS
+    mchunkptr p  = mem2chunk(oldmem);
+    mstate ms = get_mstate_for(p);
+#else /* FOOTERS */
+    mstate ms = (mstate)msp;
+#endif /* FOOTERS */
+    if (!ok_magic(ms)) {
+      USAGE_ERROR_ACTION(ms,ms);
+      return 0;
+    }
+    return internal_realloc(ms, oldmem, bytes);
+  }
+}
+
+void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) {
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  return internal_memalign(ms, alignment, bytes);
+}
+
+void** mspace_independent_calloc(mspace msp, size_t n_elements,
+                                size_t elem_size, void* chunks[]) {
+  size_t sz = elem_size; /* serves as 1-element array */
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  return ialloc(ms, n_elements, &sz, 3, chunks);
+}
+
+void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+                                  size_t sizes[], void* chunks[]) {
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  return ialloc(ms, n_elements, sizes, 0, chunks);
+}
+
+int mspace_trim(mspace msp, size_t pad) {
+  int result = 0;
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    if (!PREACTION(ms)) {
+      result = sys_trim(ms, pad);
+      POSTACTION(ms);
+    }
+  }
+  else {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+  return result;
+}
+
+void mspace_malloc_stats(mspace msp) {
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    internal_malloc_stats(ms);
+  }
+  else {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+}
+
+size_t mspace_footprint(mspace msp) {
+  size_t result = 0;
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    result = ms->footprint;
+  }
+  else {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+  return result;
+}
+
+
+size_t mspace_max_footprint(mspace msp) {
+  size_t result = 0;
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    result = ms->max_footprint;
+  }
+  else {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+  return result;
+}
+
+
+#if !NO_MALLINFO
+struct mallinfo mspace_mallinfo(mspace msp) {
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+  return internal_mallinfo(ms);
+}
+#endif /* NO_MALLINFO */
+
+size_t mspace_usable_size(void* mem) {
+  if (mem != 0) {
+    mchunkptr p = mem2chunk(mem);
+    if (cinuse(p))
+      return chunksize(p) - overhead_for(p);
+  }
+  return 0;
+}
+
+int mspace_mallopt(int param_number, int value) {
+  return change_mparam(param_number, value);
+}
+
+#endif /* MSPACES */
+
+/* -------------------- Alternative MORECORE functions ------------------- */
+
+/*
+  Guidelines for creating a custom version of MORECORE:
+
+  * For best performance, MORECORE should allocate in multiples of pagesize.
+  * MORECORE may allocate more memory than requested. (Or even less,
+      but this will usually result in a malloc failure.)
+  * MORECORE must not allocate memory when given argument zero, but
+      instead return one past the end address of memory from previous
+      nonzero call.
+  * For best performance, consecutive calls to MORECORE with positive
+      arguments should return increasing addresses, indicating that
+      space has been contiguously extended.
+  * Even though consecutive calls to MORECORE need not return contiguous
+      addresses, it must be OK for malloc'ed chunks to span multiple
+      regions in those cases where they do happen to be contiguous.
+  * MORECORE need not handle negative arguments -- it may instead
+      just return MFAIL when given negative arguments.
+      Negative arguments are always multiples of pagesize. MORECORE
+      must not misinterpret negative args as large positive unsigned
+      args. You can suppress all such calls from even occurring by defining
+      MORECORE_CANNOT_TRIM,
+
+  As an example alternative MORECORE, here is a custom allocator
+  kindly contributed for pre-OSX macOS.  It uses virtually but not
+  necessarily physically contiguous non-paged memory (locked in,
+  present and won't get swapped out).  You can use it by uncommenting
+  this section, adding some #includes, and setting up the appropriate
+  defines above:
+
+      #define MORECORE osMoreCore
+
+  There is also a shutdown routine that should somehow be called for
+  cleanup upon program exit.
+
+  #define MAX_POOL_ENTRIES 100
+  #define MINIMUM_MORECORE_SIZE  (64 * 1024U)
+  static int next_os_pool;
+  void *our_os_pools[MAX_POOL_ENTRIES];
+
+  void *osMoreCore(int size)
+  {
+    void *ptr = 0;
+    static void *sbrk_top = 0;
+
+    if (size > 0)
+    {
+      if (size < MINIMUM_MORECORE_SIZE)
+        size = MINIMUM_MORECORE_SIZE;
+      if (CurrentExecutionLevel() == kTaskLevel)
+        ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0);
+      if (ptr == 0)
+      {
+       return (void *) MFAIL;
+      }
+      // save ptrs so they can be freed during cleanup
+      our_os_pools[next_os_pool] = ptr;
+      next_os_pool++;
+      ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK);
+      sbrk_top = (char *) ptr + size;
+      return ptr;
+    }
+    else if (size < 0)
+    {
+      // we don't currently support shrink behavior
+      return (void *) MFAIL;
+    }
+    else
+    {
+      return sbrk_top;
+    }
+  }
+
+  // cleanup any allocated memory pools
+  // called as last thing before shutting down driver
+
+  void osCleanupMem(void)
+  {
+    void **ptr;
+
+    for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++)
+      if (*ptr)
+      {
+        PoolDeallocate(*ptr);
+        *ptr = 0;
+      }
+  }
+
+*/
+
+
+/* -----------------------------------------------------------------------
+History:
+    V2.8.4 (not yet released)
+      * Add mspace_mmap_large_chunks; thanks to Jean Brouwers
+      * Fix insufficient sys_alloc padding when using 16byte alignment
+      * Fix bad error check in mspace_footprint
+      * Adaptations for ptmalloc, courtesy of Wolfram Gloger.
+      * Reentrant spin locks, courtesy of Earl Chew and others
+      * Win32 improvements, courtesy of Niall Douglas and Earl Chew
+      * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options
+      * Extension hook in malloc_state
+      * Various small adjustments to reduce warnings on some compilers
+      * Various configuration extensions/changes for more platforms. Thanks
+        to all who contributed these.
+
+    V2.8.3 Thu Sep 22 11:16:32 2005  Doug Lea  (dl at gee)
+      * Add max_footprint functions
+      * Ensure all appropriate literals are size_t
+      * Fix conditional compilation problem for some #define settings
+      * Avoid concatenating segments with the one provided
+       in create_mspace_with_base
+      * Rename some variables to avoid compiler shadowing warnings
+      * Use explicit lock initialization.
+      * Better handling of sbrk interference.
+      * Simplify and fix segment insertion, trimming and mspace_destroy
+      * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x
+      * Thanks especially to Dennis Flanagan for help on these.
+
+    V2.8.2 Sun Jun 12 16:01:10 2005  Doug Lea  (dl at gee)
+      * Fix memalign brace error.
+
+    V2.8.1 Wed Jun  8 16:11:46 2005  Doug Lea  (dl at gee)
+      * Fix improper #endif nesting in C++
+      * Add explicit casts needed for C++
+
+    V2.8.0 Mon May 30 14:09:02 2005  Doug Lea  (dl at gee)
+      * Use trees for large bins
+      * Support mspaces
+      * Use segments to unify sbrk-based and mmap-based system allocation,
+       removing need for emulation on most platforms without sbrk.
+      * Default safety checks
+      * Optional footer checks. Thanks to William Robertson for the idea.
+      * Internal code refactoring
+      * Incorporate suggestions and platform-specific changes.
+       Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas,
+       Aaron Bachmann,  Emery Berger, and others.
+      * Speed up non-fastbin processing enough to remove fastbins.
+      * Remove useless cfree() to avoid conflicts with other apps.
+      * Remove internal memcpy, memset. Compilers handle builtins better.
+      * Remove some options that no one ever used and rename others.
+
+    V2.7.2 Sat Aug 17 09:07:30 2002  Doug Lea  (dl at gee)
+      * Fix malloc_state bitmap array misdeclaration
+
+    V2.7.1 Thu Jul 25 10:58:03 2002  Doug Lea  (dl at gee)
+      * Allow tuning of FIRST_SORTED_BIN_SIZE
+      * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte.
+      * Better detection and support for non-contiguousness of MORECORE.
+       Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger
+      * Bypass most of malloc if no frees. Thanks To Emery Berger.
+      * Fix freeing of old top non-contiguous chunk im sysmalloc.
+      * Raised default trim and map thresholds to 256K.
+      * Fix mmap-related #defines. Thanks to Lubos Lunak.
+      * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield.
+      * Branch-free bin calculation
+      * Default trim and mmap thresholds now 256K.
+
+    V2.7.0 Sun Mar 11 14:14:06 2001  Doug Lea  (dl at gee)
+      * Introduce independent_comalloc and independent_calloc.
+       Thanks to Michael Pachos for motivation and help.
+      * Make optional .h file available
+      * Allow > 2GB requests on 32bit systems.
+      * new WIN32 sbrk, mmap, munmap, lock code from <Walter@GeNeSys-e.de>.
+       Thanks also to Andreas Mueller <a.mueller at paradatec.de>,
+       and Anonymous.
+      * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for
+       helping test this.)
+      * memalign: check alignment arg
+      * realloc: don't try to shift chunks backwards, since this
+       leads to  more fragmentation in some programs and doesn't
+       seem to help in any others.
+      * Collect all cases in malloc requiring system memory into sysmalloc
+      * Use mmap as backup to sbrk
+      * Place all internal state in malloc_state
+      * Introduce fastbins (although similar to 2.5.1)
+      * Many minor tunings and cosmetic improvements
+      * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK
+      * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS
+       Thanks to Tony E. Bennett <tbennett@nvidia.com> and others.
+      * Include errno.h to support default failure action.
+
+    V2.6.6 Sun Dec  5 07:42:19 1999  Doug Lea  (dl at gee)
+      * return null for negative arguments
+      * Added Several WIN32 cleanups from Martin C. Fong <mcfong at yahoo.com>
+        * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h'
+         (e.g. WIN32 platforms)
+        * Cleanup header file inclusion for WIN32 platforms
+        * Cleanup code to avoid Microsoft Visual C++ compiler complaints
+        * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing
+          memory allocation routines
+        * Set 'malloc_getpagesize' for WIN32 platforms (needs more work)
+        * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to
+          usage of 'assert' in non-WIN32 code
+        * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to
+          avoid infinite loop
+      * Always call 'fREe()' rather than 'free()'
+
+    V2.6.5 Wed Jun 17 15:57:31 1998  Doug Lea  (dl at gee)
+      * Fixed ordering problem with boundary-stamping
+
+    V2.6.3 Sun May 19 08:17:58 1996  Doug Lea  (dl at gee)
+      * Added pvalloc, as recommended by H.J. Liu
+      * Added 64bit pointer support mainly from Wolfram Gloger
+      * Added anonymously donated WIN32 sbrk emulation
+      * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen
+      * malloc_extend_top: fix mask error that caused wastage after
+       foreign sbrks
+      * Add linux mremap support code from HJ Liu
+
+    V2.6.2 Tue Dec  5 06:52:55 1995  Doug Lea  (dl at gee)
+      * Integrated most documentation with the code.
+      * Add support for mmap, with help from
+       Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
+      * Use last_remainder in more cases.
+      * Pack bins using idea from  colin@nyx10.cs.du.edu
+      * Use ordered bins instead of best-fit threshhold
+      * Eliminate block-local decls to simplify tracing and debugging.
+      * Support another case of realloc via move into top
+      * Fix error occuring when initial sbrk_base not word-aligned.
+      * Rely on page size for units instead of SBRK_UNIT to
+       avoid surprises about sbrk alignment conventions.
+      * Add mallinfo, mallopt. Thanks to Raymond Nijssen
+       (raymond@es.ele.tue.nl) for the suggestion.
+      * Add `pad' argument to malloc_trim and top_pad mallopt parameter.
+      * More precautions for cases where other routines call sbrk,
+       courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
+      * Added macros etc., allowing use in linux libc from
+       H.J. Lu (hjl@gnu.ai.mit.edu)
+      * Inverted this history list
+
+    V2.6.1 Sat Dec  2 14:10:57 1995  Doug Lea  (dl at gee)
+      * Re-tuned and fixed to behave more nicely with V2.6.0 changes.
+      * Removed all preallocation code since under current scheme
+       the work required to undo bad preallocations exceeds
+       the work saved in good cases for most test programs.
+      * No longer use return list or unconsolidated bins since
+       no scheme using them consistently outperforms those that don't
+       given above changes.
+      * Use best fit for very large chunks to prevent some worst-cases.
+      * Added some support for debugging
+
+    V2.6.0 Sat Nov  4 07:05:23 1995  Doug Lea  (dl at gee)
+      * Removed footers when chunks are in use. Thanks to
+       Paul Wilson (wilson@cs.texas.edu) for the suggestion.
+
+    V2.5.4 Wed Nov  1 07:54:51 1995  Doug Lea  (dl at gee)
+      * Added malloc_trim, with help from Wolfram Gloger
+       (wmglo@Dent.MED.Uni-Muenchen.DE).
+
+    V2.5.3 Tue Apr 26 10:16:01 1994  Doug Lea  (dl at g)
+
+    V2.5.2 Tue Apr  5 16:20:40 1994  Doug Lea  (dl at g)
+      * realloc: try to expand in both directions
+      * malloc: swap order of clean-bin strategy;
+      * realloc: only conditionally expand backwards
+      * Try not to scavenge used bins
+      * Use bin counts as a guide to preallocation
+      * Occasionally bin return list chunks in first scan
+      * Add a few optimizations from colin@nyx10.cs.du.edu
+
+    V2.5.1 Sat Aug 14 15:40:43 1993  Doug Lea  (dl at g)
+      * faster bin computation & slightly different binning
+      * merged all consolidations to one part of malloc proper
+        (eliminating old malloc_find_space & malloc_clean_bin)
+      * Scan 2 returns chunks (not just 1)
+      * Propagate failure in realloc if malloc returns 0
+      * Add stuff to allow compilation on non-ANSI compilers
+         from kpv@research.att.com
+
+    V2.5 Sat Aug  7 07:41:59 1993  Doug Lea  (dl at g.oswego.edu)
+      * removed potential for odd address access in prev_chunk
+      * removed dependency on getpagesize.h
+      * misc cosmetics and a bit more internal documentation
+      * anticosmetics: mangled names in macros to evade debugger strangeness
+      * tested on sparc, hp-700, dec-mips, rs6000
+         with gcc & native cc (hp, dec only) allowing
+         Detlefs & Zorn comparison study (in SIGPLAN Notices.)
+
+    Trial version Fri Aug 28 13:14:29 1992  Doug Lea  (dl at g.oswego.edu)
+      * Based loosely on libg++-1.2X malloc. (It retains some of the overall
+        structure of old version,  but most details differ.)
+
+*/
+
+
diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c
new file mode 100644 (file)
index 0000000..d9a17a8
--- /dev/null
@@ -0,0 +1,966 @@
+/* Alternative malloc implementation for multiple threads without
+lock contention based on dlmalloc. (C) 2005-2006 Niall Douglas
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef _MSC_VER
+/* Enable full aliasing on MSVC */
+/*#pragma optimize("a", on)*/
+#endif
+
+/*#define FULLSANITYCHECKS*/
+
+#include "nedmalloc.h"
+#if defined(WIN32)
+ #include <malloc.h>
+#endif
+#define MSPACES 1
+#define ONLY_MSPACES 1
+#ifndef USE_LOCKS
+ #define USE_LOCKS 1
+#endif
+#define FOOTERS 1           /* Need to enable footers so frees lock the right mspace */
+#undef DEBUG                           /* dlmalloc wants DEBUG either 0 or 1 */
+#ifdef _DEBUG
+ #define DEBUG 1
+#else
+ #define DEBUG 0
+#endif
+#ifdef NDEBUG               /* Disable assert checking on release builds */
+ #undef DEBUG
+#endif
+/* The default of 64Kb means we spend too much time kernel-side */
+#ifndef DEFAULT_GRANULARITY
+#define DEFAULT_GRANULARITY (1*1024*1024)
+#endif
+/*#define USE_SPIN_LOCKS 0*/
+
+
+/*#define FORCEINLINE*/
+#include "malloc.c.h"
+#ifdef NDEBUG               /* Disable assert checking on release builds */
+ #undef DEBUG
+#endif
+
+/* The maximum concurrent threads in a pool possible */
+#ifndef MAXTHREADSINPOOL
+#define MAXTHREADSINPOOL 16
+#endif
+/* The maximum number of threadcaches which can be allocated */
+#ifndef THREADCACHEMAXCACHES
+#define THREADCACHEMAXCACHES 256
+#endif
+/* The maximum size to be allocated from the thread cache */
+#ifndef THREADCACHEMAX
+#define THREADCACHEMAX 8192
+#endif
+#if 0
+/* The number of cache entries for finer grained bins. This is (topbitpos(THREADCACHEMAX)-4)*2 */
+#define THREADCACHEMAXBINS ((13-4)*2)
+#else
+/* The number of cache entries. This is (topbitpos(THREADCACHEMAX)-4) */
+#define THREADCACHEMAXBINS (13-4)
+#endif
+/* Point at which the free space in a thread cache is garbage collected */
+#ifndef THREADCACHEMAXFREESPACE
+#define THREADCACHEMAXFREESPACE (512*1024)
+#endif
+
+
+#ifdef WIN32
+ #define TLSVAR                        DWORD
+ #define TLSALLOC(k)   (*(k)=TlsAlloc(), TLS_OUT_OF_INDEXES==*(k))
+ #define TLSFREE(k)            (!TlsFree(k))
+ #define TLSGET(k)             TlsGetValue(k)
+ #define TLSSET(k, a)  (!TlsSetValue(k, a))
+ #ifdef DEBUG
+static LPVOID ChkedTlsGetValue(DWORD idx)
+{
+       LPVOID ret=TlsGetValue(idx);
+       assert(S_OK==GetLastError());
+       return ret;
+}
+  #undef TLSGET
+  #define TLSGET(k) ChkedTlsGetValue(k)
+ #endif
+#else
+ #define TLSVAR                        pthread_key_t
+ #define TLSALLOC(k)   pthread_key_create(k, 0)
+ #define TLSFREE(k)            pthread_key_delete(k)
+ #define TLSGET(k)             pthread_getspecific(k)
+ #define TLSSET(k, a)  pthread_setspecific(k, a)
+#endif
+
+#if 0
+/* Only enable if testing with valgrind. Causes misoperation */
+#define mspace_malloc(p, s) malloc(s)
+#define mspace_realloc(p, m, s) realloc(m, s)
+#define mspace_calloc(p, n, s) calloc(n, s)
+#define mspace_free(p, m) free(m)
+#endif
+
+
+#if defined(__cplusplus)
+#if !defined(NO_NED_NAMESPACE)
+namespace nedalloc {
+#else
+extern "C" {
+#endif
+#endif
+
+size_t nedblksize(void *mem) THROWSPEC
+{
+#if 0
+       /* Only enable if testing with valgrind. Causes misoperation */
+       return THREADCACHEMAX;
+#else
+       if(mem)
+       {
+               mchunkptr p=mem2chunk(mem);
+               assert(cinuse(p));      /* If this fails, someone tried to free a block twice */
+               if(cinuse(p))
+                       return chunksize(p)-overhead_for(p);
+       }
+       return 0;
+#endif
+}
+
+void nedsetvalue(void *v) THROWSPEC                                    { nedpsetvalue(0, v); }
+void * nedmalloc(size_t size) THROWSPEC                                { return nedpmalloc(0, size); }
+void * nedcalloc(size_t no, size_t size) THROWSPEC     { return nedpcalloc(0, no, size); }
+void * nedrealloc(void *mem, size_t size) THROWSPEC    { return nedprealloc(0, mem, size); }
+void   nedfree(void *mem) THROWSPEC                                    { nedpfree(0, mem); }
+void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC { return nedpmemalign(0, alignment, bytes); }
+#if !NO_MALLINFO
+struct mallinfo nedmallinfo(void) THROWSPEC                    { return nedpmallinfo(0); }
+#endif
+int    nedmallopt(int parno, int value) THROWSPEC      { return nedpmallopt(0, parno, value); }
+int    nedmalloc_trim(size_t pad) THROWSPEC                    { return nedpmalloc_trim(0, pad); }
+void   nedmalloc_stats() THROWSPEC                                     { nedpmalloc_stats(0); }
+size_t nedmalloc_footprint() THROWSPEC                         { return nedpmalloc_footprint(0); }
+void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC { return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
+void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC   { return nedpindependent_comalloc(0, elems, sizes, chunks); }
+
+struct threadcacheblk_t;
+typedef struct threadcacheblk_t threadcacheblk;
+struct threadcacheblk_t
+{      /* Keep less than 16 bytes on 32 bit systems and 32 bytes on 64 bit systems */
+#ifdef FULLSANITYCHECKS
+       unsigned int magic;
+#endif
+       unsigned int lastUsed, size;
+       threadcacheblk *next, *prev;
+};
+typedef struct threadcache_t
+{
+#ifdef FULLSANITYCHECKS
+       unsigned int magic1;
+#endif
+       int mymspace;                                           /* Last mspace entry this thread used */
+       long threadid;
+       unsigned int mallocs, frees, successes;
+       size_t freeInCache;                                     /* How much free space is stored in this cache */
+       threadcacheblk *bins[(THREADCACHEMAXBINS+1)*2];
+#ifdef FULLSANITYCHECKS
+       unsigned int magic2;
+#endif
+} threadcache;
+struct nedpool_t
+{
+       MLOCK_T mutex;
+       void *uservalue;
+       int threads;                                            /* Max entries in m to use */
+       threadcache *caches[THREADCACHEMAXCACHES];
+       TLSVAR mycache;                                         /* Thread cache for this thread. 0 for unset, negative for use mspace-1 directly, otherwise is cache-1 */
+       mstate m[MAXTHREADSINPOOL+1];           /* mspace entries for this pool */
+};
+static nedpool syspool;
+
+static FORCEINLINE unsigned int size2binidx(size_t _size) THROWSPEC
+{      /* 8=1000       16=10000        20=10100        24=11000        32=100000       48=110000       4096=1000000000000 */
+       unsigned int topbit, size=(unsigned int)(_size>>4);
+       /* 16=1         20=1    24=1    32=10   48=11   64=100  96=110  128=1000        4096=100000000 */
+
+#if defined(__GNUC__)
+       topbit = sizeof(size)*__CHAR_BIT__ - 1 - __builtin_clz(size);
+#elif defined(_MSC_VER) && _MSC_VER>=1300
+       {
+           unsigned long bsrTopBit;
+
+           _BitScanReverse(&bsrTopBit, size);
+
+           topbit = bsrTopBit;
+       }
+#else
+#if 0
+       union {
+               unsigned asInt[2];
+               double asDouble;
+       };
+       int n;
+
+       asDouble = (double)size + 0.5;
+       topbit = (asInt[!FOX_BIGENDIAN] >> 20) - 1023;
+#else
+       {
+               unsigned int x=size;
+               x = x | (x >> 1);
+               x = x | (x >> 2);
+               x = x | (x >> 4);
+               x = x | (x >> 8);
+               x = x | (x >>16);
+               x = ~x;
+               x = x - ((x >> 1) & 0x55555555);
+               x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+               x = (x + (x >> 4)) & 0x0F0F0F0F;
+               x = x + (x << 8);
+               x = x + (x << 16);
+               topbit=31 - (x >> 24);
+       }
+#endif
+#endif
+       return topbit;
+}
+
+
+#ifdef FULLSANITYCHECKS
+static void tcsanitycheck(threadcacheblk **ptr) THROWSPEC
+{
+       assert((ptr[0] && ptr[1]) || (!ptr[0] && !ptr[1]));
+       if(ptr[0] && ptr[1])
+       {
+               assert(nedblksize(ptr[0])>=sizeof(threadcacheblk));
+               assert(nedblksize(ptr[1])>=sizeof(threadcacheblk));
+               assert(*(unsigned int *) "NEDN"==ptr[0]->magic);
+               assert(*(unsigned int *) "NEDN"==ptr[1]->magic);
+               assert(!ptr[0]->prev);
+               assert(!ptr[1]->next);
+               if(ptr[0]==ptr[1])
+               {
+                       assert(!ptr[0]->next);
+                       assert(!ptr[1]->prev);
+               }
+       }
+}
+static void tcfullsanitycheck(threadcache *tc) THROWSPEC
+{
+       threadcacheblk **tcbptr=tc->bins;
+       int n;
+       for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
+       {
+               threadcacheblk *b, *ob=0;
+               tcsanitycheck(tcbptr);
+               for(b=tcbptr[0]; b; ob=b, b=b->next)
+               {
+                       assert(*(unsigned int *) "NEDN"==b->magic);
+                       assert(!ob || ob->next==b);
+                       assert(!ob || b->prev==ob);
+               }
+       }
+}
+#endif
+
+static NOINLINE void RemoveCacheEntries(nedpool *p, threadcache *tc, unsigned int age) THROWSPEC
+{
+#ifdef FULLSANITYCHECKS
+       tcfullsanitycheck(tc);
+#endif
+       if(tc->freeInCache)
+       {
+               threadcacheblk **tcbptr=tc->bins;
+               int n;
+               for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
+               {
+                       threadcacheblk **tcb=tcbptr+1;          /* come from oldest end of list */
+                       /*tcsanitycheck(tcbptr);*/
+                       for(; *tcb && tc->frees-(*tcb)->lastUsed>=age; )
+                       {
+                               threadcacheblk *f=*tcb;
+                               size_t blksize=f->size; /*nedblksize(f);*/
+                               assert(blksize<=nedblksize(f));
+                               assert(blksize);
+#ifdef FULLSANITYCHECKS
+                               assert(*(unsigned int *) "NEDN"==(*tcb)->magic);
+#endif
+                               *tcb=(*tcb)->prev;
+                               if(*tcb)
+                                       (*tcb)->next=0;
+                               else
+                                       *tcbptr=0;
+                               tc->freeInCache-=blksize;
+                               assert((long) tc->freeInCache>=0);
+                               mspace_free(0, f);
+                               /*tcsanitycheck(tcbptr);*/
+                       }
+               }
+       }
+#ifdef FULLSANITYCHECKS
+       tcfullsanitycheck(tc);
+#endif
+}
+static void DestroyCaches(nedpool *p) THROWSPEC
+{
+       if(p->caches)
+       {
+               threadcache *tc;
+               int n;
+               for(n=0; n<THREADCACHEMAXCACHES; n++)
+               {
+                       if((tc=p->caches[n]))
+                       {
+                               tc->frees++;
+                               RemoveCacheEntries(p, tc, 0);
+                               assert(!tc->freeInCache);
+                               tc->mymspace=-1;
+                               tc->threadid=0;
+                               mspace_free(0, tc);
+                               p->caches[n]=0;
+                       }
+               }
+       }
+}
+
+static NOINLINE threadcache *AllocCache(nedpool *p) THROWSPEC
+{
+       threadcache *tc=0;
+       int n, end;
+       ACQUIRE_LOCK(&p->mutex);
+       for(n=0; n<THREADCACHEMAXCACHES && p->caches[n]; n++);
+       if(THREADCACHEMAXCACHES==n)
+       {       /* List exhausted, so disable for this thread */
+               RELEASE_LOCK(&p->mutex);
+               return 0;
+       }
+       tc=p->caches[n]=(threadcache *) mspace_calloc(p->m[0], 1, sizeof(threadcache));
+       if(!tc)
+       {
+               RELEASE_LOCK(&p->mutex);
+               return 0;
+       }
+#ifdef FULLSANITYCHECKS
+       tc->magic1=*(unsigned int *)"NEDMALC1";
+       tc->magic2=*(unsigned int *)"NEDMALC2";
+#endif
+       tc->threadid=(long)(size_t)CURRENT_THREAD;
+       for(end=0; p->m[end]; end++);
+       tc->mymspace=tc->threadid % end;
+       RELEASE_LOCK(&p->mutex);
+       if(TLSSET(p->mycache, (void *)(size_t)(n+1))) abort();
+       return tc;
+}
+
+static void *threadcache_malloc(nedpool *p, threadcache *tc, size_t *size) THROWSPEC
+{
+       void *ret=0;
+       unsigned int bestsize;
+       unsigned int idx=size2binidx(*size);
+       size_t blksize=0;
+       threadcacheblk *blk, **binsptr;
+#ifdef FULLSANITYCHECKS
+       tcfullsanitycheck(tc);
+#endif
+       /* Calculate best fit bin size */
+       bestsize=1<<(idx+4);
+#if 0
+       /* Finer grained bin fit */
+       idx<<=1;
+       if(*size>bestsize)
+       {
+               idx++;
+               bestsize+=bestsize>>1;
+       }
+       if(*size>bestsize)
+       {
+               idx++;
+               bestsize=1<<(4+(idx>>1));
+       }
+#else
+       if(*size>bestsize)
+       {
+               idx++;
+               bestsize<<=1;
+       }
+#endif
+       assert(bestsize>=*size);
+       if(*size<bestsize) *size=bestsize;
+       assert(*size<=THREADCACHEMAX);
+       assert(idx<=THREADCACHEMAXBINS);
+       binsptr=&tc->bins[idx*2];
+       /* Try to match close, but move up a bin if necessary */
+       blk=*binsptr;
+       if(!blk || blk->size<*size)
+       {       /* Bump it up a bin */
+               if(idx<THREADCACHEMAXBINS)
+               {
+                       idx++;
+                       binsptr+=2;
+                       blk=*binsptr;
+               }
+       }
+       if(blk)
+       {
+               blksize=blk->size; /*nedblksize(blk);*/
+               assert(nedblksize(blk)>=blksize);
+               assert(blksize>=*size);
+               if(blk->next)
+                       blk->next->prev=0;
+               *binsptr=blk->next;
+               if(!*binsptr)
+                       binsptr[1]=0;
+#ifdef FULLSANITYCHECKS
+               blk->magic=0;
+#endif
+               assert(binsptr[0]!=blk && binsptr[1]!=blk);
+               assert(nedblksize(blk)>=sizeof(threadcacheblk) && nedblksize(blk)<=THREADCACHEMAX+CHUNK_OVERHEAD);
+               /*printf("malloc: %p, %p, %p, %lu\n", p, tc, blk, (long) size);*/
+               ret=(void *) blk;
+       }
+       ++tc->mallocs;
+       if(ret)
+       {
+               assert(blksize>=*size);
+               ++tc->successes;
+               tc->freeInCache-=blksize;
+               assert((long) tc->freeInCache>=0);
+       }
+#if defined(DEBUG) && 0
+       if(!(tc->mallocs & 0xfff))
+       {
+               printf("*** threadcache=%u, mallocs=%u (%f), free=%u (%f), freeInCache=%u\n", (unsigned int) tc->threadid, tc->mallocs,
+                       (float) tc->successes/tc->mallocs, tc->frees, (float) tc->successes/tc->frees, (unsigned int) tc->freeInCache);
+       }
+#endif
+#ifdef FULLSANITYCHECKS
+       tcfullsanitycheck(tc);
+#endif
+       return ret;
+}
+static NOINLINE void ReleaseFreeInCache(nedpool *p, threadcache *tc, int mymspace) THROWSPEC
+{
+       unsigned int age=THREADCACHEMAXFREESPACE/8192;
+       /*ACQUIRE_LOCK(&p->m[mymspace]->mutex);*/
+       while(age && tc->freeInCache>=THREADCACHEMAXFREESPACE)
+       {
+               RemoveCacheEntries(p, tc, age);
+               /*printf("*** Removing cache entries older than %u (%u)\n", age, (unsigned int) tc->freeInCache);*/
+               age>>=1;
+       }
+       /*RELEASE_LOCK(&p->m[mymspace]->mutex);*/
+}
+static void threadcache_free(nedpool *p, threadcache *tc, int mymspace, void *mem, size_t size) THROWSPEC
+{
+       unsigned int bestsize;
+       unsigned int idx=size2binidx(size);
+       threadcacheblk **binsptr, *tck=(threadcacheblk *) mem;
+       assert(size>=sizeof(threadcacheblk) && size<=THREADCACHEMAX+CHUNK_OVERHEAD);
+#ifdef DEBUG
+       {       /* Make sure this is a valid memory block */
+           mchunkptr p  = mem2chunk(mem);
+           mstate fm = get_mstate_for(p);
+           if (!ok_magic(fm)) {
+             USAGE_ERROR_ACTION(fm, p);
+             return;
+           }
+       }
+#endif
+#ifdef FULLSANITYCHECKS
+       tcfullsanitycheck(tc);
+#endif
+       /* Calculate best fit bin size */
+       bestsize=1<<(idx+4);
+#if 0
+       /* Finer grained bin fit */
+       idx<<=1;
+       if(size>bestsize)
+       {
+               unsigned int biggerbestsize=bestsize+bestsize<<1;
+               if(size>=biggerbestsize)
+               {
+                       idx++;
+                       bestsize=biggerbestsize;
+               }
+       }
+#endif
+       if(bestsize!=size)      /* dlmalloc can round up, so we round down to preserve indexing */
+               size=bestsize;
+       binsptr=&tc->bins[idx*2];
+       assert(idx<=THREADCACHEMAXBINS);
+       if(tck==*binsptr)
+       {
+               fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", tck);
+               abort();
+       }
+#ifdef FULLSANITYCHECKS
+       tck->magic=*(unsigned int *) "NEDN";
+#endif
+       tck->lastUsed=++tc->frees;
+       tck->size=(unsigned int) size;
+       tck->next=*binsptr;
+       tck->prev=0;
+       if(tck->next)
+               tck->next->prev=tck;
+       else
+               binsptr[1]=tck;
+       assert(!*binsptr || (*binsptr)->size==tck->size);
+       *binsptr=tck;
+       assert(tck==tc->bins[idx*2]);
+       assert(tc->bins[idx*2+1]==tck || binsptr[0]->next->prev==tck);
+       /*printf("free: %p, %p, %p, %lu\n", p, tc, mem, (long) size);*/
+       tc->freeInCache+=size;
+#ifdef FULLSANITYCHECKS
+       tcfullsanitycheck(tc);
+#endif
+#if 1
+       if(tc->freeInCache>=THREADCACHEMAXFREESPACE)
+               ReleaseFreeInCache(p, tc, mymspace);
+#endif
+}
+
+
+
+
+static NOINLINE int InitPool(nedpool *p, size_t capacity, int threads) THROWSPEC
+{      /* threads is -1 for system pool */
+       ensure_initialization();
+       ACQUIRE_MALLOC_GLOBAL_LOCK();
+       if(p->threads) goto done;
+       if(INITIAL_LOCK(&p->mutex)) goto err;
+       if(TLSALLOC(&p->mycache)) goto err;
+       if(!(p->m[0]=(mstate) create_mspace(capacity, 1))) goto err;
+       p->m[0]->extp=p;
+       p->threads=(threads<1 || threads>MAXTHREADSINPOOL) ? MAXTHREADSINPOOL : threads;
+done:
+       RELEASE_MALLOC_GLOBAL_LOCK();
+       return 1;
+err:
+       if(threads<0)
+               abort();                        /* If you can't allocate for system pool, we're screwed */
+       DestroyCaches(p);
+       if(p->m[0])
+       {
+               destroy_mspace(p->m[0]);
+               p->m[0]=0;
+       }
+       if(p->mycache)
+       {
+               if(TLSFREE(p->mycache)) abort();
+               p->mycache=0;
+       }
+       RELEASE_MALLOC_GLOBAL_LOCK();
+       return 0;
+}
+static NOINLINE mstate FindMSpace(nedpool *p, threadcache *tc, int *lastUsed, size_t size) THROWSPEC
+{      /* Gets called when thread's last used mspace is in use. The strategy
+       is to run through the list of all available mspaces looking for an
+       unlocked one and if we fail, we create a new one so long as we don't
+       exceed p->threads */
+       int n, end;
+       for(n=end=*lastUsed+1; p->m[n]; end=++n)
+       {
+               if(TRY_LOCK(&p->m[n]->mutex)) goto found;
+       }
+       for(n=0; n<*lastUsed && p->m[n]; n++)
+       {
+               if(TRY_LOCK(&p->m[n]->mutex)) goto found;
+       }
+       if(end<p->threads)
+       {
+               mstate temp;
+               if(!(temp=(mstate) create_mspace(size, 1)))
+                       goto badexit;
+               /* Now we're ready to modify the lists, we lock */
+               ACQUIRE_LOCK(&p->mutex);
+               while(p->m[end] && end<p->threads)
+                       end++;
+               if(end>=p->threads)
+               {       /* Drat, must destroy it now */
+                       RELEASE_LOCK(&p->mutex);
+                       destroy_mspace((mspace) temp);
+                       goto badexit;
+               }
+               /* We really want to make sure this goes into memory now but we
+               have to be careful of breaking aliasing rules, so write it twice */
+               *((volatile struct malloc_state **) &p->m[end])=p->m[end]=temp;
+               ACQUIRE_LOCK(&p->m[end]->mutex);
+               /*printf("Created mspace idx %d\n", end);*/
+               RELEASE_LOCK(&p->mutex);
+               n=end;
+               goto found;
+       }
+       /* Let it lock on the last one it used */
+badexit:
+       ACQUIRE_LOCK(&p->m[*lastUsed]->mutex);
+       return p->m[*lastUsed];
+found:
+       *lastUsed=n;
+       if(tc)
+               tc->mymspace=n;
+       else
+       {
+               if(TLSSET(p->mycache, (void *)(size_t)(-(n+1)))) abort();
+       }
+       return p->m[n];
+}
+
+nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC
+{
+       nedpool *ret;
+       if(!(ret=(nedpool *) nedpcalloc(0, 1, sizeof(nedpool)))) return 0;
+       if(!InitPool(ret, capacity, threads))
+       {
+               nedpfree(0, ret);
+               return 0;
+       }
+       return ret;
+}
+void neddestroypool(nedpool *p) THROWSPEC
+{
+       int n;
+       ACQUIRE_LOCK(&p->mutex);
+       DestroyCaches(p);
+       for(n=0; p->m[n]; n++)
+       {
+               destroy_mspace(p->m[n]);
+               p->m[n]=0;
+       }
+       RELEASE_LOCK(&p->mutex);
+       if(TLSFREE(p->mycache)) abort();
+       nedpfree(0, p);
+}
+
+void nedpsetvalue(nedpool *p, void *v) THROWSPEC
+{
+       if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+       p->uservalue=v;
+}
+void *nedgetvalue(nedpool **p, void *mem) THROWSPEC
+{
+       nedpool *np=0;
+       mchunkptr mcp=mem2chunk(mem);
+       mstate fm;
+       if(!(is_aligned(chunk2mem(mcp))) && mcp->head != FENCEPOST_HEAD) return 0;
+       if(!cinuse(mcp)) return 0;
+       if(!next_pinuse(mcp)) return 0;
+       if(!is_mmapped(mcp) && !pinuse(mcp))
+       {
+               if(next_chunk(prev_chunk(mcp))!=mcp) return 0;
+       }
+       fm=get_mstate_for(mcp);
+       if(!ok_magic(fm)) return 0;
+       if(!ok_address(fm, mcp)) return 0;
+       if(!fm->extp) return 0;
+       np=(nedpool *) fm->extp;
+       if(p) *p=np;
+       return np->uservalue;
+}
+
+void neddisablethreadcache(nedpool *p) THROWSPEC
+{
+       int mycache;
+       if(!p)
+       {
+               p=&syspool;
+               if(!syspool.threads) InitPool(&syspool, 0, -1);
+       }
+       mycache=(int)(size_t) TLSGET(p->mycache);
+       if(!mycache)
+       {       /* Set to mspace 0 */
+               if(TLSSET(p->mycache, (void *)-1)) abort();
+       }
+       else if(mycache>0)
+       {       /* Set to last used mspace */
+               threadcache *tc=p->caches[mycache-1];
+#if defined(DEBUG)
+               printf("Threadcache utilisation: %lf%% in cache with %lf%% lost to other threads\n",
+                       100.0*tc->successes/tc->mallocs, 100.0*((double) tc->mallocs-tc->frees)/tc->mallocs);
+#endif
+               if(TLSSET(p->mycache, (void *)(size_t)(-tc->mymspace))) abort();
+               tc->frees++;
+               RemoveCacheEntries(p, tc, 0);
+               assert(!tc->freeInCache);
+               tc->mymspace=-1;
+               tc->threadid=0;
+               mspace_free(0, p->caches[mycache-1]);
+               p->caches[mycache-1]=0;
+       }
+}
+
+#define GETMSPACE(m,p,tc,ms,s,action)           \
+  do                                            \
+  {                                             \
+    mstate m = GetMSpace((p),(tc),(ms),(s));    \
+    action;                                     \
+    RELEASE_LOCK(&m->mutex);                    \
+  } while (0)
+
+static FORCEINLINE mstate GetMSpace(nedpool *p, threadcache *tc, int mymspace, size_t size) THROWSPEC
+{      /* Returns a locked and ready for use mspace */
+       mstate m=p->m[mymspace];
+       assert(m);
+       if(!TRY_LOCK(&p->m[mymspace]->mutex)) m=FindMSpace(p, tc, &mymspace, size);\
+       /*assert(IS_LOCKED(&p->m[mymspace]->mutex));*/
+       return m;
+}
+static FORCEINLINE void GetThreadCache(nedpool **p, threadcache **tc, int *mymspace, size_t *size) THROWSPEC
+{
+       int mycache;
+       if(size && *size<sizeof(threadcacheblk)) *size=sizeof(threadcacheblk);
+       if(!*p)
+       {
+               *p=&syspool;
+               if(!syspool.threads) InitPool(&syspool, 0, -1);
+       }
+       mycache=(int)(size_t) TLSGET((*p)->mycache);
+       if(mycache>0)
+       {
+               *tc=(*p)->caches[mycache-1];
+               *mymspace=(*tc)->mymspace;
+       }
+       else if(!mycache)
+       {
+               *tc=AllocCache(*p);
+               if(!*tc)
+               {       /* Disable */
+                       if(TLSSET((*p)->mycache, (void *)-1)) abort();
+                       *mymspace=0;
+               }
+               else
+                       *mymspace=(*tc)->mymspace;
+       }
+       else
+       {
+               *tc=0;
+               *mymspace=-mycache-1;
+       }
+       assert(*mymspace>=0);
+       assert((long)(size_t)CURRENT_THREAD==(*tc)->threadid);
+#ifdef FULLSANITYCHECKS
+       if(*tc)
+       {
+               if(*(unsigned int *)"NEDMALC1"!=(*tc)->magic1 || *(unsigned int *)"NEDMALC2"!=(*tc)->magic2)
+               {
+                       abort();
+               }
+       }
+#endif
+}
+
+void * nedpmalloc(nedpool *p, size_t size) THROWSPEC
+{
+       void *ret=0;
+       threadcache *tc;
+       int mymspace;
+       GetThreadCache(&p, &tc, &mymspace, &size);
+#if THREADCACHEMAX
+       if(tc && size<=THREADCACHEMAX)
+       {       /* Use the thread cache */
+               ret=threadcache_malloc(p, tc, &size);
+       }
+#endif
+       if(!ret)
+       {       /* Use this thread's mspace */
+       GETMSPACE(m, p, tc, mymspace, size,
+                 ret=mspace_malloc(m, size));
+       }
+       return ret;
+}
+void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC
+{
+       size_t rsize=size*no;
+       void *ret=0;
+       threadcache *tc;
+       int mymspace;
+       GetThreadCache(&p, &tc, &mymspace, &rsize);
+#if THREADCACHEMAX
+       if(tc && rsize<=THREADCACHEMAX)
+       {       /* Use the thread cache */
+               if((ret=threadcache_malloc(p, tc, &rsize)))
+                       memset(ret, 0, rsize);
+       }
+#endif
+       if(!ret)
+       {       /* Use this thread's mspace */
+       GETMSPACE(m, p, tc, mymspace, rsize,
+                 ret=mspace_calloc(m, 1, rsize));
+       }
+       return ret;
+}
+void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC
+{
+       void *ret=0;
+       threadcache *tc;
+       int mymspace;
+       if(!mem) return nedpmalloc(p, size);
+       GetThreadCache(&p, &tc, &mymspace, &size);
+#if THREADCACHEMAX
+       if(tc && size && size<=THREADCACHEMAX)
+       {       /* Use the thread cache */
+               size_t memsize=nedblksize(mem);
+               assert(memsize);
+               if((ret=threadcache_malloc(p, tc, &size)))
+               {
+                       memcpy(ret, mem, memsize<size ? memsize : size);
+                       if(memsize<=THREADCACHEMAX)
+                               threadcache_free(p, tc, mymspace, mem, memsize);
+                       else
+                               mspace_free(0, mem);
+               }
+       }
+#endif
+       if(!ret)
+       {       /* Reallocs always happen in the mspace they happened in, so skip
+               locking the preferred mspace for this thread */
+               ret=mspace_realloc(0, mem, size);
+       }
+       return ret;
+}
+void   nedpfree(nedpool *p, void *mem) THROWSPEC
+{      /* Frees always happen in the mspace they happened in, so skip
+       locking the preferred mspace for this thread */
+       threadcache *tc;
+       int mymspace;
+       size_t memsize;
+       assert(mem);
+       GetThreadCache(&p, &tc, &mymspace, 0);
+#if THREADCACHEMAX
+       memsize=nedblksize(mem);
+       assert(memsize);
+       if(mem && tc && memsize<=(THREADCACHEMAX+CHUNK_OVERHEAD))
+               threadcache_free(p, tc, mymspace, mem, memsize);
+       else
+#endif
+               mspace_free(0, mem);
+}
+void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC
+{
+       void *ret;
+       threadcache *tc;
+       int mymspace;
+       GetThreadCache(&p, &tc, &mymspace, &bytes);
+       {       /* Use this thread's mspace */
+       GETMSPACE(m, p, tc, mymspace, bytes,
+                 ret=mspace_memalign(m, alignment, bytes));
+       }
+       return ret;
+}
+#if !NO_MALLINFO
+struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC
+{
+       int n;
+       struct mallinfo ret={0};
+       if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+       for(n=0; p->m[n]; n++)
+       {
+               struct mallinfo t=mspace_mallinfo(p->m[n]);
+               ret.arena+=t.arena;
+               ret.ordblks+=t.ordblks;
+               ret.hblkhd+=t.hblkhd;
+               ret.usmblks+=t.usmblks;
+               ret.uordblks+=t.uordblks;
+               ret.fordblks+=t.fordblks;
+               ret.keepcost+=t.keepcost;
+       }
+       return ret;
+}
+#endif
+int    nedpmallopt(nedpool *p, int parno, int value) THROWSPEC
+{
+       return mspace_mallopt(parno, value);
+}
+int    nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC
+{
+       int n, ret=0;
+       if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+       for(n=0; p->m[n]; n++)
+       {
+               ret+=mspace_trim(p->m[n], pad);
+       }
+       return ret;
+}
+void   nedpmalloc_stats(nedpool *p) THROWSPEC
+{
+       int n;
+       if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+       for(n=0; p->m[n]; n++)
+       {
+               mspace_malloc_stats(p->m[n]);
+       }
+}
+size_t nedpmalloc_footprint(nedpool *p) THROWSPEC
+{
+       size_t ret=0;
+       int n;
+       if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+       for(n=0; p->m[n]; n++)
+       {
+               ret+=mspace_footprint(p->m[n]);
+       }
+       return ret;
+}
+void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC
+{
+       void **ret;
+       threadcache *tc;
+       int mymspace;
+       GetThreadCache(&p, &tc, &mymspace, &elemsize);
+    GETMSPACE(m, p, tc, mymspace, elemsno*elemsize,
+             ret=mspace_independent_calloc(m, elemsno, elemsize, chunks));
+       return ret;
+}
+void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC
+{
+       void **ret;
+       threadcache *tc;
+       int mymspace;
+    size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
+    if(!adjustedsizes) return 0;
+    for(i=0; i<elems; i++)
+       adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
+       GetThreadCache(&p, &tc, &mymspace, 0);
+       GETMSPACE(m, p, tc, mymspace, 0,
+             ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
+       return ret;
+}
+
+#ifdef OVERRIDE_STRDUP
+/*
+ * This implementation is purely there to override the libc version, to
+ * avoid a crash due to allocation and free on different 'heaps'.
+ */
+char *strdup(const char *s1)
+{
+       char *s2 = 0;
+       if (s1) {
+               s2 = malloc(strlen(s1) + 1);
+               strcpy(s2, s1);
+       }
+       return s2;
+}
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/compat/nedmalloc/nedmalloc.h b/compat/nedmalloc/nedmalloc.h
new file mode 100644 (file)
index 0000000..f960e66
--- /dev/null
@@ -0,0 +1,180 @@
+/* nedalloc, an alternative malloc implementation for multiple threads without
+lock contention based on dlmalloc v2.8.3. (C) 2005 Niall Douglas
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef NEDMALLOC_H
+#define NEDMALLOC_H
+
+
+/* See malloc.c.h for what each function does.
+
+REPLACE_SYSTEM_ALLOCATOR causes nedalloc's functions to be called malloc,
+free etc. instead of nedmalloc, nedfree etc. You may or may not want this.
+
+NO_NED_NAMESPACE prevents the functions from being defined in the nedalloc
+namespace when in C++ (uses the global namespace instead).
+
+EXTSPEC can be defined to be __declspec(dllexport) or
+__attribute__ ((visibility("default"))) or whatever you like. It defaults
+to extern.
+
+USE_LOCKS can be 2 if you want to define your own MLOCK_T, INITIAL_LOCK,
+ACQUIRE_LOCK, RELEASE_LOCK, TRY_LOCK, IS_LOCKED and NULL_LOCK_INITIALIZER.
+
+*/
+
+#include <stddef.h>   /* for size_t */
+
+#ifndef EXTSPEC
+ #define EXTSPEC extern
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER>=1400
+ #define MALLOCATTR __declspec(restrict)
+#endif
+#ifdef __GNUC__
+ #define MALLOCATTR __attribute__ ((malloc))
+#endif
+#ifndef MALLOCATTR
+ #define MALLOCATTR
+#endif
+
+#ifdef REPLACE_SYSTEM_ALLOCATOR
+ #define nedmalloc               malloc
+ #define nedcalloc               calloc
+ #define nedrealloc              realloc
+ #define nedfree                 free
+ #define nedmemalign             memalign
+ #define nedmallinfo             mallinfo
+ #define nedmallopt              mallopt
+ #define nedmalloc_trim          malloc_trim
+ #define nedmalloc_stats         malloc_stats
+ #define nedmalloc_footprint     malloc_footprint
+ #define nedindependent_calloc   independent_calloc
+ #define nedindependent_comalloc independent_comalloc
+ #ifdef _MSC_VER
+  #define nedblksize              _msize
+ #endif
+#endif
+
+#ifndef NO_MALLINFO
+#define NO_MALLINFO 0
+#endif
+
+#if !NO_MALLINFO
+struct mallinfo;
+#endif
+
+#if defined(__cplusplus)
+ #if !defined(NO_NED_NAMESPACE)
+namespace nedalloc {
+ #else
+extern "C" {
+ #endif
+ #define THROWSPEC throw()
+#else
+ #define THROWSPEC
+#endif
+
+/* These are the global functions */
+
+/* Gets the usable size of an allocated block. Note this will always be bigger than what was
+asked for due to rounding etc.
+*/
+EXTSPEC size_t nedblksize(void *mem) THROWSPEC;
+
+EXTSPEC void nedsetvalue(void *v) THROWSPEC;
+
+EXTSPEC MALLOCATTR void * nedmalloc(size_t size) THROWSPEC;
+EXTSPEC MALLOCATTR void * nedcalloc(size_t no, size_t size) THROWSPEC;
+EXTSPEC MALLOCATTR void * nedrealloc(void *mem, size_t size) THROWSPEC;
+EXTSPEC void   nedfree(void *mem) THROWSPEC;
+EXTSPEC MALLOCATTR void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC;
+#if !NO_MALLINFO
+EXTSPEC struct mallinfo nedmallinfo(void) THROWSPEC;
+#endif
+EXTSPEC int    nedmallopt(int parno, int value) THROWSPEC;
+EXTSPEC int    nedmalloc_trim(size_t pad) THROWSPEC;
+EXTSPEC void   nedmalloc_stats(void) THROWSPEC;
+EXTSPEC size_t nedmalloc_footprint(void) THROWSPEC;
+EXTSPEC MALLOCATTR void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC;
+EXTSPEC MALLOCATTR void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC;
+
+/* These are the pool functions */
+struct nedpool_t;
+typedef struct nedpool_t nedpool;
+
+/* Creates a memory pool for use with the nedp* functions below.
+Capacity is how much to allocate immediately (if you know you'll be allocating a lot
+of memory very soon) which you can leave at zero. Threads specifies how many threads
+will *normally* be accessing the pool concurrently. Setting this to zero means it
+extends on demand, but be careful of this as it can rapidly consume system resources
+where bursts of concurrent threads use a pool at once.
+*/
+EXTSPEC MALLOCATTR nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC;
+
+/* Destroys a memory pool previously created by nedcreatepool().
+*/
+EXTSPEC void neddestroypool(nedpool *p) THROWSPEC;
+
+/* Sets a value to be associated with a pool. You can retrieve this value by passing
+any memory block allocated from that pool.
+*/
+EXTSPEC void nedpsetvalue(nedpool *p, void *v) THROWSPEC;
+/* Gets a previously set value using nedpsetvalue() or zero if memory is unknown.
+Optionally can also retrieve pool.
+*/
+EXTSPEC void *nedgetvalue(nedpool **p, void *mem) THROWSPEC;
+
+/* Disables the thread cache for the calling thread, returning any existing cache
+data to the central pool.
+*/
+EXTSPEC void neddisablethreadcache(nedpool *p) THROWSPEC;
+
+EXTSPEC MALLOCATTR void * nedpmalloc(nedpool *p, size_t size) THROWSPEC;
+EXTSPEC MALLOCATTR void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC;
+EXTSPEC MALLOCATTR void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC;
+EXTSPEC void   nedpfree(nedpool *p, void *mem) THROWSPEC;
+EXTSPEC MALLOCATTR void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC;
+#if !NO_MALLINFO
+EXTSPEC struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC;
+#endif
+EXTSPEC int    nedpmallopt(nedpool *p, int parno, int value) THROWSPEC;
+EXTSPEC int    nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC;
+EXTSPEC void   nedpmalloc_stats(nedpool *p) THROWSPEC;
+EXTSPEC size_t nedpmalloc_footprint(nedpool *p) THROWSPEC;
+EXTSPEC MALLOCATTR void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC;
+EXTSPEC MALLOCATTR void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#undef MALLOCATTR
+#undef EXTSPEC
+
+#endif
index 357e733074ea7c85f880fa577ad65dfb3787fec7..6c0fb056a571b30627e404d7f164de31ed1a8699 100644 (file)
@@ -6,8 +6,12 @@
  * number of characters to write without the trailing NUL.
  */
 #ifndef SNPRINTF_SIZE_CORR
+#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ < 4
+#define SNPRINTF_SIZE_CORR 1
+#else
 #define SNPRINTF_SIZE_CORR 0
 #endif
+#endif
 
 #undef vsnprintf
 int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
index 44dc293ad314379d7835d9d96a5c3fd12ad2b27f..9217c24b43f7546815079d540304aa71821df926 100644 (file)
@@ -80,6 +80,7 @@ static void set_console_attr(void)
 static void erase_in_line(void)
 {
        CONSOLE_SCREEN_BUFFER_INFO sbi;
+       DWORD dummy; /* Needed for Windows 7 (or Vista) regression */
 
        if (!console)
                return;
@@ -87,7 +88,7 @@ static void erase_in_line(void)
        GetConsoleScreenBufferInfo(console, &sbi);
        FillConsoleOutputCharacterA(console, ' ',
                sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
-               NULL);
+               &dummy);
 }
 
 
index 7cce0c12d507b222d47d8469abf59bf3ef9096b9..dd6045131880111bb09306cb3e48330a0e5acdac 100644 (file)
@@ -30,8 +30,10 @@ NEEDS_SSL_WITH_CRYPTO=@NEEDS_SSL_WITH_CRYPTO@
 NO_OPENSSL=@NO_OPENSSL@
 NO_CURL=@NO_CURL@
 NO_EXPAT=@NO_EXPAT@
+NO_LIBGEN_H=@NO_LIBGEN_H@
 NEEDS_LIBICONV=@NEEDS_LIBICONV@
 NEEDS_SOCKET=@NEEDS_SOCKET@
+NEEDS_RESOLV=@NEEDS_RESOLV@
 NO_SYS_SELECT_H=@NO_SYS_SELECT_H@
 NO_D_INO_IN_DIRENT=@NO_D_INO_IN_DIRENT@
 NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@
@@ -46,6 +48,7 @@ NO_STRTOUMAX=@NO_STRTOUMAX@
 NO_SETENV=@NO_SETENV@
 NO_UNSETENV=@NO_UNSETENV@
 NO_MKDTEMP=@NO_MKDTEMP@
+NO_MKSTEMPS=@NO_MKSTEMPS@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
index 25fbe3bb6be2b29501965409670b066ae616c570..1885674e396d31cc625d34d029835e15b279dc8d 100644 (file)
@@ -475,6 +475,15 @@ AC_CHECK_LIB([c], [socket],
 AC_SUBST(NEEDS_SOCKET)
 test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket"
 
+#
+# Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough.
+# Notably on Solaris hstrerror resides in libresolv and on Solaris 7
+# inet_ntop and inet_pton additionally reside there.
+AC_CHECK_LIB([resolv], [hstrerror],
+[NEEDS_RESOLV=],
+[NEEDS_RESOLV=YesPlease])
+AC_SUBST(NEEDS_RESOLV)
+test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv"
 
 ## Checks for header files.
 AC_MSG_NOTICE([CHECKS for header files])
@@ -635,6 +644,12 @@ AC_SUBST(SNPRINTF_RETURNS_BOGUS)
 ## (in default C library and libraries checked by AC_CHECK_LIB)
 AC_MSG_NOTICE([CHECKS for library functions])
 #
+# Define NO_LIBGEN_H if you don't have libgen.h.
+AC_CHECK_HEADER([libgen.h],
+[NO_LIBGEN_H=],
+[NO_LIBGEN_H=YesPlease])
+AC_SUBST(NO_LIBGEN_H)
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=],
@@ -685,6 +700,13 @@ GIT_CHECK_FUNC(mkdtemp,
 [NO_MKDTEMP=YesPlease])
 AC_SUBST(NO_MKDTEMP)
 #
+# Define NO_MKSTEMPS if you don't have mkstemps in the C library.
+GIT_CHECK_FUNC(mkstemps,
+[NO_MKSTEMPS=],
+[NO_MKSTEMPS=YesPlease])
+AC_SUBST(NO_MKSTEMPS)
+#
+#
 # Define NO_MMAP if you want to avoid mmap.
 #
 # Define NO_ICONV if your libc does not properly support iconv.
index 958c831e430340435c31b640bd757d56b7cb8b71..0ce941ef444277957efdd8a359a568775e2b17a4 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -605,14 +605,18 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                die("command line too long");
 
        conn->in = conn->out = -1;
-       conn->argv = arg = xcalloc(6, sizeof(*arg));
+       conn->argv = arg = xcalloc(7, sizeof(*arg));
        if (protocol == PROTO_SSH) {
                const char *ssh = getenv("GIT_SSH");
+               int putty = ssh && strcasestr(ssh, "plink");
                if (!ssh) ssh = "ssh";
 
                *arg++ = ssh;
+               if (putty && !strcasestr(ssh, "tortoiseplink"))
+                       *arg++ = "-batch";
                if (port) {
-                       *arg++ = "-p";
+                       /* P is for PuTTY, p is for OpenSSH */
+                       *arg++ = putty ? "-P" : "-p";
                        *arg++ = port;
                }
                *arg++ = host;
index f44152c4331bb6a2de764e6dcc98c1ac39e517e5..b60cb68a8bd19ddfdce06f136121189b2bad0185 100755 (executable)
 #       with the bash.showDirtyState variable, which defaults to true
 #       once GIT_PS1_SHOWDIRTYSTATE is enabled.
 #
+#       You can also see if currently something is stashed, by setting
+#       GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
+#       then a '$' will be shown next to the branch name.
+#
 # To submit patches:
 #
 #    *) Read Documentation/SubmittingPatches
@@ -84,26 +88,24 @@ __git_ps1 ()
        if [ -n "$g" ]; then
                local r
                local b
-               if [ -d "$g/rebase-apply" ]; then
-                       if [ -f "$g/rebase-apply/rebasing" ]; then
-                               r="|REBASE"
-               elif [ -f "$g/rebase-apply/applying" ]; then
-                               r="|AM"
-                       else
-                               r="|AM/REBASE"
-                       fi
-                       b="$(git symbolic-ref HEAD 2>/dev/null)"
-               elif [ -f "$g/rebase-merge/interactive" ]; then
+               if [ -f "$g/rebase-merge/interactive" ]; then
                        r="|REBASE-i"
                        b="$(cat "$g/rebase-merge/head-name")"
                elif [ -d "$g/rebase-merge" ]; then
                        r="|REBASE-m"
                        b="$(cat "$g/rebase-merge/head-name")"
                else
-                       if [ -f "$g/MERGE_HEAD" ]; then
+                       if [ -d "$g/rebase-apply" ]; then
+                               if [ -f "$g/rebase-apply/rebasing" ]; then
+                                       r="|REBASE"
+                               elif [ -f "$g/rebase-apply/applying" ]; then
+                                       r="|AM"
+                               else
+                                       r="|AM/REBASE"
+                               fi
+                       elif [ -f "$g/MERGE_HEAD" ]; then
                                r="|MERGING"
-                       fi
-                       if [ -f "$g/BISECT_LOG" ]; then
+                       elif [ -f "$g/BISECT_LOG" ]; then
                                r="|BISECTING"
                        fi
 
@@ -129,6 +131,7 @@ __git_ps1 ()
 
                local w
                local i
+               local s
                local c
 
                if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
@@ -150,14 +153,15 @@ __git_ps1 ()
                                        fi
                                fi
                        fi
+                       if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
+                               git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+                       fi
                fi
 
-               if [ -n "$b" ]; then
-                       if [ -n "${1-}" ]; then
-                               printf "$1" "$c${b##refs/heads/}$w$i$r"
-                       else
-                               printf " (%s)" "$c${b##refs/heads/}$w$i$r"
-                       fi
+               if [ -n "${1-}" ]; then
+                       printf "$1" "$c${b##refs/heads/}$w$i$s$r"
+               else
+                       printf " (%s)" "$c${b##refs/heads/}$w$i$s$r"
                fi
        fi
 }
@@ -923,7 +927,7 @@ _git_diff ()
 }
 
 __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
-                       tkdiff vimdiff gvimdiff xxdiff
+                       tkdiff vimdiff gvimdiff xxdiff araxis
 "
 
 _git_difftool ()
@@ -1295,7 +1299,7 @@ _git_rebase ()
 }
 
 __git_send_email_confirm_options="always never auto cc compose"
-__git_send_email_suppresscc_options="author self cc ccbody sob cccmd body all"
+__git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all"
 
 _git_send_email ()
 {
@@ -1334,6 +1338,35 @@ _git_send_email ()
        COMPREPLY=()
 }
 
+__git_config_get_set_variables ()
+{
+       local prevword word config_file= c=$COMP_CWORD
+       while [ $c -gt 1 ]; do
+               word="${COMP_WORDS[c]}"
+               case "$word" in
+               --global|--system|--file=*)
+                       config_file="$word"
+                       break
+                       ;;
+               -f|--file)
+                       config_file="$word $prevword"
+                       break
+                       ;;
+               esac
+               prevword=$word
+               c=$((--c))
+       done
+
+       for i in $(git --git-dir="$(__gitdir)" config $config_file --list \
+                       2>/dev/null); do
+               case "$i" in
+               *.*)
+                       echo "${i/=*/}"
+                       ;;
+               esac
+       done
+}
+
 _git_config ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -1365,7 +1398,8 @@ _git_config ()
                __gitcomp "$(__git_merge_strategies)"
                return
                ;;
-       color.branch|color.diff|color.interactive|color.status|color.ui)
+       color.branch|color.diff|color.interactive|\
+       color.showbranch|color.status|color.ui)
                __gitcomp "always never auto"
                return
                ;;
@@ -1400,6 +1434,10 @@ _git_config ()
                __gitcomp "$__git_send_email_suppresscc_options"
                return
                ;;
+       --get|--get-all|--unset|--unset-all)
+               __gitcomp "$(__git_config_get_set_variables)"
+               return
+               ;;
        *.*)
                COMPREPLY=()
                return
@@ -1510,6 +1548,7 @@ _git_config ()
                color.interactive.help
                color.interactive.prompt
                color.pager
+               color.showbranch
                color.status
                color.status.added
                color.status.changed
@@ -1821,6 +1860,7 @@ _git_show_branch ()
                __gitcomp "
                        --all --remotes --topo-order --current --more=
                        --list --independent --merge-base --no-name
+                       --color --no-color
                        --sha1-name --sparse --topics --reflog
                        "
                return
@@ -1841,10 +1881,10 @@ _git_stash ()
                save,--*)
                        __gitcomp "--keep-index"
                        ;;
-               apply,--*)
+               apply,--*|pop,--*)
                        __gitcomp "--index"
                        ;;
-               show,--*|drop,--*|pop,--*|branch,--*)
+               show,--*|drop,--*|branch,--*)
                        COMPREPLY=()
                        ;;
                show,*|apply,*|drop,*|pop,*|branch,*)
index 6309d146e74a428520d09cbff4dc8d9a429ec001..78e40d2a13e5c754b10a8e68641236bf58394e05 100755 (executable)
@@ -82,10 +82,16 @@ foreach my $tar_file (@ARGV)
                $mtime = oct $mtime;
                next if $typeflag == 5; # directory
 
-               print FI "blob\n", "mark :$next_mark\n", "data $size\n";
-               while ($size > 0 && read(I, $_, 512) == 512) {
-                       print FI substr($_, 0, $size);
-                       $size -= 512;
+               print FI "blob\n", "mark :$next_mark\n";
+               if ($typeflag == 2) { # symbolic link
+                       print FI "data ", length($linkname), "\n", $linkname;
+                       $mode = 0120000;
+               } else {
+                       print FI "data $size\n";
+                       while ($size > 0 && read(I, $_, 512) == 512) {
+                               print FI substr($_, 0, $size);
+                               $size -= 512;
+                       }
                }
                print FI "\n";
 
@@ -118,7 +124,8 @@ EOF
        {
                my ($mark, $mode) = @{$files{$path}};
                $path =~ s,^([^/]+)/,, if $have_top_dir;
-               printf FI "M %o :%i %s\n", $mode & 0111 ? 0755 : 0644, $mark, $path;
+               $mode = $mode & 0111 ? 0755 : 0644 unless $mode == 0120000;
+               printf FI "M %o :%i %s\n", $mode, $mark, $path;
        }
        print FI "\n";
 
old mode 100644 (file)
new mode 100755 (executable)
index 60cbab6..2a66063
 #   --pretty %s", displaying the commit id, author, date and log
 #   message.  To list full patches separated by a blank line, you
 #   could set this to "git show -C %s; echo".
+#   To list a gitweb/cgit URL *and* a full patch for each change set, use this:
+#     "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo"
+#   Be careful if "..." contains things that will be expanded by shell "eval"
+#   or printf.
 #
 # Notes
 # -----
index e6fd8a7441a7ac6753d93e7156b9f71fe248262d..2f8a63e38881587fe29fcb72a5272ef54b9efa6e 100644 (file)
@@ -18,7 +18,7 @@ static void *insert_decoration(struct decoration *n, const struct object *base,
 {
        int size = n->size;
        struct object_decoration *hash = n->hash;
-       int j = hash_obj(base, size);
+       unsigned int j = hash_obj(base, size);
 
        while (hash[j].base) {
                if (hash[j].base == base) {
@@ -70,7 +70,7 @@ void *add_decoration(struct decoration *n, const struct object *obj,
 /* Lookup a decoration pointer */
 void *lookup_decoration(struct decoration *n, const struct object *obj)
 {
-       int j;
+       unsigned int j;
 
        /* nothing to lookup */
        if (!n->size)
diff --git a/delta.h b/delta.h
index 40ccf5a1e95f62d840a006274f7024fa43208b1c..b9d333dd5a1c64ab35159ed608cf942951504f84 100644 (file)
--- a/delta.h
+++ b/delta.h
@@ -90,12 +90,11 @@ static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
                                               const unsigned char *top)
 {
        const unsigned char *data = *datap;
-       unsigned char cmd;
-       unsigned long size = 0;
+       unsigned long cmd, size = 0;
        int i = 0;
        do {
                cmd = *data++;
-               size |= (cmd & ~0x80) << i;
+               size |= (cmd & 0x7f) << i;
                i += 7;
        } while (cmd & 0x80 && data < top);
        *datap = data;
diff --git a/diff.c b/diff.c
index d24bff1e465d5b5e486ab461bd0548387736f109..43835d756c2c2c7ec579e1c38804fe33a706944f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -839,10 +839,9 @@ static int scale_linear(int it, int width, int max_change)
 }
 
 static void show_name(FILE *file,
-                     const char *prefix, const char *name, int len,
-                     const char *reset, const char *set)
+                     const char *prefix, const char *name, int len)
 {
-       fprintf(file, " %s%s%-*s%s |", set, prefix, len, name, reset);
+       fprintf(file, " %s%-*s |", prefix, len, name);
 }
 
 static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset)
@@ -956,7 +955,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                }
 
                if (data->files[i]->is_binary) {
-                       show_name(options->file, prefix, name, len, reset, set);
+                       show_name(options->file, prefix, name, len);
                        fprintf(options->file, "  Bin ");
                        fprintf(options->file, "%s%d%s", del_c, deleted, reset);
                        fprintf(options->file, " -> ");
@@ -966,7 +965,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        continue;
                }
                else if (data->files[i]->is_unmerged) {
-                       show_name(options->file, prefix, name, len, reset, set);
+                       show_name(options->file, prefix, name, len);
                        fprintf(options->file, "  Unmerged\n");
                        continue;
                }
@@ -988,7 +987,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        add = scale_linear(add, width, max_change);
                        del = scale_linear(del, width, max_change);
                }
-               show_name(options->file, prefix, name, len, reset, set);
+               show_name(options->file, prefix, name, len);
                fprintf(options->file, "%5d%s", added + deleted,
                                added + deleted ? " " : "");
                show_graph(options->file, '+', add, add_c, reset);
@@ -996,8 +995,8 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                fprintf(options->file, "\n");
        }
        fprintf(options->file,
-              "%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
-              set, total_files, adds, dels, reset);
+              " %d files changed, %d insertions(+), %d deletions(-)\n",
+              total_files, adds, dels);
 }
 
 static void show_shortstats(struct diffstat_t* data, struct diff_options *options)
@@ -1965,8 +1964,16 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
 {
        int fd;
        struct strbuf buf = STRBUF_INIT;
+       struct strbuf template = STRBUF_INIT;
+       char *path_dup = xstrdup(path);
+       const char *base = basename(path_dup);
+
+       /* Generate "XXXXXX_basename.ext" */
+       strbuf_addstr(&template, "XXXXXX_");
+       strbuf_addstr(&template, base);
 
-       fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX");
+       fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf,
+                       strlen(base) + 1);
        if (fd < 0)
                die("unable to create temp-file: %s", strerror(errno));
        if (convert_to_working_tree(path,
@@ -1982,6 +1989,8 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        temp->hex[40] = 0;
        sprintf(temp->mode, "%06o", mode);
        strbuf_release(&buf);
+       strbuf_release(&template);
+       free(path_dup);
 }
 
 static struct diff_tempfile *prepare_temp_file(const char *name,
@@ -2015,18 +2024,15 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                        die("stat(%s): %s", name, strerror(errno));
                }
                if (S_ISLNK(st.st_mode)) {
-                       int ret;
-                       char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */
-                       ret = readlink(name, buf, sizeof(buf));
-                       if (ret < 0)
+                       struct strbuf sb = STRBUF_INIT;
+                       if (strbuf_readlink(&sb, name, st.st_size) < 0)
                                die("readlink(%s)", name);
-                       if (ret == sizeof(buf))
-                               die("symlink too long: %s", name);
-                       prep_temp_blob(name, temp, buf, ret,
+                       prep_temp_blob(name, temp, sb.buf, sb.len,
                                       (one->sha1_valid ?
                                        one->sha1 : null_sha1),
                                       (one->sha1_valid ?
                                        one->mode : S_IFLNK));
+                       strbuf_release(&sb);
                }
                else {
                        /* we can borrow from the file in the work tree */
@@ -3590,6 +3596,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
        if (start_command(&child) != 0 ||
            strbuf_read(&buf, child.out, 0) < 0 ||
            finish_command(&child) != 0) {
+               strbuf_release(&buf);
                remove_tempfile();
                error("error running textconv command '%s'", pgm);
                return NULL;
diff --git a/dir.c b/dir.c
index 0e6b752cd5833bfb970619983b4efa880aaaf134..bbfcb566e63310d38848b0b76076fb051a7446c8 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -396,7 +396,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathna
 
 static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
 {
-       if (cache_name_pos(pathname, len) >= 0)
+       if (!cache_name_is_other(pathname, len))
                return NULL;
 
        ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc);
index 6d1848b6cce89e4953a3ca6e1b2e6e1611277a4a..578780be138b216dc89333e1fde87ddcb91e5e54 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -44,11 +44,7 @@ else
 fi
 
 sq () {
-       for sqarg
-       do
-               printf "%s" "$sqarg" |
-               sed -e 's/'\''/'\''\\'\'''\''/g' -e 's/.*/ '\''&'\''/'
-       done
+       git rev-parse --sq-quote "$@"
 }
 
 stop_here () {
index 24712ff304af89317793fa4c54d39f4c579bb345..8969553658bb5f4f2191f370062af9af33989d88 100755 (executable)
@@ -33,16 +33,6 @@ require_work_tree
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 
-sq() {
-       @@PERL@@ -e '
-               for (@ARGV) {
-                       s/'\''/'\'\\\\\'\''/g;
-                       print " '\''$_'\''";
-               }
-               print "\n";
-       ' "$@"
-}
-
 bisect_autostart() {
        test -s "$GIT_DIR/BISECT_START" || {
                echo >&2 'You need to start by "git bisect start"'
@@ -107,7 +97,7 @@ bisect_start() {
        for arg; do
            case "$arg" in --) has_double_dash=1; break ;; esac
        done
-       orig_args=$(sq "$@")
+       orig_args=$(git rev-parse --sq-quote "$@")
        bad_seen=0
        eval=''
        while [ $# -gt 0 ]; do
@@ -147,7 +137,7 @@ bisect_start() {
        # Write new start state.
        #
        echo "$start_head" >"$GIT_DIR/BISECT_START" &&
-       sq "$@" >"$GIT_DIR/BISECT_NAMES" &&
+       git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
        eval "$eval" &&
        echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
        #
@@ -177,10 +167,6 @@ is_expected_rev() {
        test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
 }
 
-mark_expected_rev() {
-       echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV"
-}
-
 check_expected_revs() {
        for _rev in "$@"; do
                if ! is_expected_rev "$_rev"; then
@@ -199,7 +185,7 @@ bisect_skip() {
             *..*)
                 revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
             *)
-                revs=$(sq "$arg") ;;
+                revs=$(git rev-parse --sq-quote "$arg") ;;
            esac
             all="$all $revs"
         done
@@ -279,162 +265,22 @@ bisect_auto_next() {
        bisect_next_check && bisect_next || :
 }
 
-exit_if_skipped_commits () {
-       _tried=$1
-       _bad=$2
-       if test -n "$_tried" ; then
-               echo "There are only 'skip'ped commit left to test."
-               echo "The first bad commit could be any of:"
-               echo "$_tried" | tr '[|]' '[\012]'
-               test -n "$_bad" && echo "$_bad"
-               echo "We cannot bisect more!"
-               exit 2
-       fi
-}
-
-bisect_checkout() {
-       _rev="$1"
-       _msg="$2"
-       echo "Bisecting: $_msg"
-       mark_expected_rev "$_rev"
-       git checkout -q "$_rev" -- || exit
-       git show-branch "$_rev"
-}
-
-is_among() {
-       _rev="$1"
-       _list="$2"
-       case "$_list" in *$_rev*) return 0 ;; esac
-       return 1
-}
-
-handle_bad_merge_base() {
-       _badmb="$1"
-       _good="$2"
-       if is_expected_rev "$_badmb"; then
-               cat >&2 <<EOF
-The merge base $_badmb is bad.
-This means the bug has been fixed between $_badmb and [$_good].
-EOF
-               exit 3
-       else
-               cat >&2 <<EOF
-Some good revs are not ancestor of the bad rev.
-git bisect cannot work properly in this case.
-Maybe you mistake good and bad revs?
-EOF
-               exit 1
-       fi
-}
-
-handle_skipped_merge_base() {
-       _mb="$1"
-       _bad="$2"
-       _good="$3"
-       cat >&2 <<EOF
-Warning: the merge base between $_bad and [$_good] must be skipped.
-So we cannot be sure the first bad commit is between $_mb and $_bad.
-We continue anyway.
-EOF
-}
-
-#
-# "check_merge_bases" checks that merge bases are not "bad".
-#
-# - If one is "good", that's good, we have nothing to do.
-# - If one is "bad", it means the user assumed something wrong
-# and we must exit.
-# - If one is "skipped", we can't know but we should warn.
-# - If we don't know, we should check it out and ask the user to test.
-#
-# In the last case we will return 1, and otherwise 0.
-#
-check_merge_bases() {
-       _bad="$1"
-       _good="$2"
-       _skip="$3"
-       for _mb in $(git merge-base --all $_bad $_good)
-       do
-               if is_among "$_mb" "$_good"; then
-                       continue
-               elif test "$_mb" = "$_bad"; then
-                       handle_bad_merge_base "$_bad" "$_good"
-               elif is_among "$_mb" "$_skip"; then
-                       handle_skipped_merge_base "$_mb" "$_bad" "$_good"
-               else
-                       bisect_checkout "$_mb" "a merge base must be tested"
-                       return 1
-               fi
-       done
-       return 0
-}
-
-#
-# "check_good_are_ancestors_of_bad" checks that all "good" revs are
-# ancestor of the "bad" rev.
-#
-# If that's not the case, we need to check the merge bases.
-# If a merge base must be tested by the user we return 1 and
-# otherwise 0.
-#
-check_good_are_ancestors_of_bad() {
-       test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
-               return
-
-       _bad="$1"
-       _good=$(echo $2 | sed -e 's/\^//g')
-       _skip="$3"
-
-       # Bisecting with no good rev is ok
-       test -z "$_good" && return
-
-       _side=$(git rev-list $_good ^$_bad)
-       if test -n "$_side"; then
-               # Return if a checkout was done
-               check_merge_bases "$_bad" "$_good" "$_skip" || return
-       fi
-
-       : > "$GIT_DIR/BISECT_ANCESTORS_OK"
-
-       return 0
-}
-
 bisect_next() {
        case "$#" in 0) ;; *) usage ;; esac
        bisect_autostart
        bisect_next_check good
 
-       # Get bad, good and skipped revs
-       bad=$(git rev-parse --verify refs/bisect/bad) &&
-       good=$(git for-each-ref --format='^%(objectname)' \
-               "refs/bisect/good-*" | tr '\012' ' ') &&
-       skip=$(git for-each-ref --format='%(objectname)' \
-               "refs/bisect/skip-*" | tr '\012' ' ') || exit
-
-       # Maybe some merge bases must be tested first
-       check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
-       # Return now if a checkout has already been done
-       test "$?" -eq "1" && return
-
-       # Get bisection information
-       eval=$(eval "git bisect--helper --next-vars") &&
-       eval "$eval" || exit
-
-       if [ -z "$bisect_rev" ]; then
-               # We should exit here only if the "bad"
-               # commit is also a "skip" commit (see above).
-               exit_if_skipped_commits "$bisect_tried"
-               echo "$bad was both good and bad"
-               exit 1
-       fi
-       if [ "$bisect_rev" = "$bad" ]; then
-               exit_if_skipped_commits "$bisect_tried" "$bad"
-               echo "$bisect_rev is first bad commit"
-               git diff-tree --pretty $bisect_rev
-               exit 0
-       fi
+       # Perform all bisection computation, display and checkout
+       git bisect--helper --next-all
+       res=$?
+
+        # Check if we should exit because bisection is finished
+       test $res -eq 10 && exit 0
 
-       bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)"
+       # Check for an error in the bisection process
+       test $res -ne 0 && exit $res
+
+       return 0
 }
 
 bisect_visualize() {
index c7cf2d5d9cdbfd4ed20c8b8ea49a36af7c138a4e..919b7f1ade9cab3c4ba907f9506e3cb518fb9e7f 100644 (file)
@@ -7,7 +7,7 @@
 /*
  * See if our compiler is known to support flexible array members.
  */
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && (!defined(__SUNPRO_C) || (__SUNPRO_C > 0x580))
 # define FLEX_ARRAY /* empty */
 #elif defined(__GNUC__)
 # if (__GNUC__ >= 3)
 /* Approximation of the length of the decimal representation of this type. */
 #define decimal_length(x)      ((int)(sizeof(x) * 2.56 + 0.5) + 1)
 
-#if !defined(__APPLE__) && !defined(__FreeBSD__)  && !defined(__USLC__) && !defined(_M_UNIX)
+#if defined(__sun__)
+ /*
+  * On Solaris, when _XOPEN_EXTENDED is set, its header file
+  * forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE
+  * setting to say we are XPG5 or XPG6.  Also on Solaris,
+  * XPG6 programs must be compiled with a c99 compiler, while
+  * non XPG6 programs must be compiled with a pre-c99 compiler.
+  */
+# if __STDC_VERSION__ - 0 >= 199901L
+# define _XOPEN_SOURCE 600
+# else
+# define _XOPEN_SOURCE 500
+# endif
+#elif !defined(__APPLE__) && !defined(__FreeBSD__)  && !defined(__USLC__) && !defined(_M_UNIX)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
+#ifndef __sun__
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif
+#endif
 #define _ALL_SOURCE 1
 #define _GNU_SOURCE 1
 #define _BSD_SOURCE 1
 #include "compat/mingw.h"
 #endif /* __MINGW32__ */
 
+#ifndef NO_LIBGEN_H
+#include <libgen.h>
+#else
+#define basename gitbasename
+extern char *gitbasename(char *);
+#endif
+
 #ifndef NO_ICONV
 #include <iconv.h>
 #endif
@@ -232,6 +254,11 @@ extern int gitsetenv(const char *, const char *, int);
 extern char *gitmkdtemp(char *);
 #endif
 
+#ifdef NO_MKSTEMPS
+#define mkstemps gitmkstemps
+extern int gitmkstemps(char *, int);
+#endif
+
 #ifdef NO_UNSETENV
 #define unsetenv gitunsetenv
 extern void gitunsetenv(const char *);
index 6d9f0ef0f989133422cf8c0302e63dab15a999d5..a36df3392652b8a854ca48bdf71977dcf12b244b 100755 (executable)
@@ -8,9 +8,9 @@ use File::Basename qw(basename dirname);
 use File::Spec;
 use Git;
 
-our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w, $opt_W);
+our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w, $opt_W, $opt_k);
 
-getopts('uhPpvcfam:d:w:W');
+getopts('uhPpvcfkam:d:w:W');
 
 $opt_h && usage();
 
@@ -225,7 +225,14 @@ if (@canstatusfiles) {
       foreach my $name (keys %todo) {
        my $basename = basename($name);
 
-       $basename = "no file " . $basename if (exists($added{$basename}));
+       # CVS reports files that don't exist in the current revision as
+       # "no file $basename" in its "status" output, so we should
+       # anticipate that.  Totally unknown files will have a status
+       # "Unknown". However, if they exist in the Attic, their status
+       # will be "Up-to-date" (this means they were added once but have
+       # been removed).
+       $basename = "no file $basename" if $added{$basename};
+
        $basename =~ s/^\s+//;
        $basename =~ s/\s+$//;
 
@@ -233,31 +240,45 @@ if (@canstatusfiles) {
          $fullname{$basename} = $name;
          push (@canstatusfiles2, $name);
          delete($todo{$name});
-        }
+       }
       }
       my @cvsoutput;
       @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles2);
       foreach my $l (@cvsoutput) {
-        chomp $l;
-        if ($l =~ /^File:\s+(.*\S)\s+Status: (.*)$/) {
-         if (!exists($fullname{$1})) {
-           print STDERR "Huh? Status reported for unexpected file '$1'\n";
-         } else {
-           $cvsstat{$fullname{$1}} = $2;
-         }
-       }
+       chomp $l;
+       next unless
+           my ($file, $status) = $l =~ /^File:\s+(.*\S)\s+Status: (.*)$/;
+
+       my $fullname = $fullname{$file};
+       print STDERR "Huh? Status '$status' reported for unexpected file '$file'\n"
+           unless defined $fullname;
+
+       # This response means the file does not exist except in
+       # CVS's attic, so set the status accordingly
+       $status = "In-attic"
+           if $file =~ /^no file /
+               && $status eq 'Up-to-date';
+
+       $cvsstat{$fullname{$file}} = $status;
       }
     }
 }
 
-# ... validate new files,
+# ... Validate that new files have the correct status
 foreach my $f (@afiles) {
-    if (defined ($cvsstat{$f}) and $cvsstat{$f} ne "Unknown") {
-       $dirty = 1;
+    next unless defined(my $stat = $cvsstat{$f});
+
+    # This means the file has never been seen before
+    next if $stat eq 'Unknown';
+
+    # This means the file has been seen before but was removed
+    next if $stat eq 'In-attic';
+
+    $dirty = 1;
        warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n";
        warn "Status was: $cvsstat{$f}\n";
-    }
 }
+
 # ... validate known files.
 foreach my $f (@files) {
     next if grep { $_ eq $f } @afiles;
@@ -266,7 +287,26 @@ foreach my $f (@files) {
        $dirty = 1;
        warn "File $f not up to date but has status '$cvsstat{$f}' in your CVS checkout!\n";
     }
+
+    # Depending on how your GIT tree got imported from CVS you may
+    # have a conflict between expanded keywords in your CVS tree and
+    # unexpanded keywords in the patch about to be applied.
+    if ($opt_k) {
+       my $orig_file ="$f.orig";
+       rename $f, $orig_file;
+       open(FILTER_IN, "<$orig_file") or die "Cannot open $orig_file\n";
+       open(FILTER_OUT, ">$f") or die "Cannot open $f\n";
+       while (<FILTER_IN>)
+       {
+           my $line = $_;
+           $line =~ s/\$([A-Z][a-z]+):[^\$]+\$/\$\1\$/g;
+           print FILTER_OUT $line;
+       }
+       close FILTER_IN;
+       close FILTER_OUT;
+    }
 }
+
 if ($dirty) {
     if ($opt_f) {      warn "The tree is not clean -- forced merge\n";
        $dirty = 0;
@@ -370,7 +410,7 @@ sleep(1);
 
 sub usage {
        print STDERR <<END;
-Usage: GIT_DIR=/path/to/.git git cvsexportcommit [-h] [-p] [-v] [-c] [-f] [-u] [-w cvsworkdir] [-m msgprefix] [ parent ] commit
+Usage: GIT_DIR=/path/to/.git git cvsexportcommit [-h] [-p] [-v] [-c] [-f] [-u] [-k] [-w cvsworkdir] [-m msgprefix] [ parent ] commit
 END
        exit(1);
 }
index a16a2795d70779365212c09b6d0dd42d1746842b..bfb01f7842e6192e0b3fbc0e3f2ab96269578289 100644 (file)
@@ -18,6 +18,9 @@ translate_merge_tool_path () {
        emerge)
                echo emacs
                ;;
+       araxis)
+               echo compare
+               ;;
        *)
                echo "$1"
                ;;
@@ -43,7 +46,7 @@ check_unchanged () {
 valid_tool () {
        case "$1" in
        kdiff3 | tkdiff | xxdiff | meld | opendiff | \
-       emerge | vimdiff | gvimdiff | ecmerge | diffuse)
+       emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis)
                ;; # happy
        tortoisemerge)
                if ! merge_mode; then
@@ -228,8 +231,8 @@ run_merge_tool () {
                        fi
                        check_unchanged
                else
-                       "$merge_tool_path" "$LOCAL" "$REMOTE" \
-                               --default --mode=merge2 --to="$MERGED"
+                       "$merge_tool_path" --default --mode=diff2 \
+                               "$LOCAL" "$REMOTE"
                fi
                ;;
        emerge)
@@ -248,7 +251,7 @@ run_merge_tool () {
                        status=$?
                else
                        "$merge_tool_path" -f emerge-files-command \
-                               "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
+                               "$LOCAL" "$REMOTE"
                fi
                ;;
        tortoisemerge)
@@ -263,6 +266,24 @@ run_merge_tool () {
                        status=1
                fi
                ;;
+       araxis)
+               if merge_mode; then
+                       touch "$BACKUP"
+                       if $base_present; then
+                               "$merge_tool_path" -wait -merge -3 -a1 \
+                                       "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
+                                       >/dev/null 2>&1
+                       else
+                               "$merge_tool_path" -wait -2 \
+                                       "$LOCAL" "$REMOTE" "$MERGED" \
+                                       >/dev/null 2>&1
+                       fi
+                       check_unchanged
+               else
+                       "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
+                               >/dev/null 2>&1
+               fi
+               ;;
        *)
                merge_tool_cmd="$(get_merge_tool_cmd "$1")"
                if test -z "$merge_tool_cmd"; then
@@ -302,7 +323,7 @@ guess_merge_tool () {
                else
                        tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
                fi
-               tools="$tools gvimdiff diffuse ecmerge"
+               tools="$tools gvimdiff diffuse ecmerge araxis"
        fi
        if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then
                # $EDITOR is emacs so add emerge as a candidate
index a296719861ec3b7aab5e530e67dc99b58267a56a..5f47b18141a0758c3d0dae2cfe764495c92139c5 100755 (executable)
@@ -60,205 +60,36 @@ get_default_remote () {
        echo ${origin:-origin}
 }
 
-get_remote_default_refs_for_push () {
-       data_source=$(get_data_source "$1")
-       case "$data_source" in
-       '' | branches | self)
-               ;; # no default push mapping, just send matching refs.
-       config)
-               git config --get-all "remote.$1.push" ;;
-       remotes)
-               sed -ne '/^Push: */{
-                       s///p
-               }' "$GIT_DIR/remotes/$1" ;;
-       *)
-               die "internal error: get-remote-default-ref-for-push $1" ;;
-       esac
-}
-
-# Called from canon_refs_list_for_fetch -d "$remote", which
-# is called from get_remote_default_refs_for_fetch to grok
-# refspecs that are retrieved from the configuration, but not
-# from get_remote_refs_for_fetch when it deals with refspecs
-# supplied on the command line.  $ls_remote_result has the list
-# of refs available at remote.
-#
-# The first token returned is either "explicit" or "glob"; this
-# is to help prevent randomly "globbed" ref from being chosen as
-# a merge candidate
-expand_refs_wildcard () {
-       echo "$ls_remote_result" |
-       git fetch--tool expand-refs-wildcard "-" "$@"
-}
-
-# Subroutine to canonicalize remote:local notation.
-canon_refs_list_for_fetch () {
-       # If called from get_remote_default_refs_for_fetch
-       # leave the branches in branch.${curr_branch}.merge alone,
-       # or the first one otherwise; add prefix . to the rest
-       # to prevent the secondary branches to be merged by default.
-       merge_branches=
-       curr_branch=
-       if test "$1" = "-d"
-       then
-               shift ; remote="$1" ; shift
-               set $(expand_refs_wildcard "$remote" "$@")
-               is_explicit="$1"
-               shift
-               if test "$remote" = "$(get_default_remote)"
-               then
-                       curr_branch=$(git symbolic-ref -q HEAD | \
-                           sed -e 's|^refs/heads/||')
-                       merge_branches=$(git config \
-                           --get-all "branch.${curr_branch}.merge")
-               fi
-               if test -z "$merge_branches" && test $is_explicit != explicit
-               then
-                       merge_branches=..this.will.never.match.any.ref..
-               fi
-       fi
-       for ref
-       do
-               force=
-               case "$ref" in
-               +*)
-                       ref=$(expr "z$ref" : 'z+\(.*\)')
-                       force=+
-                       ;;
-               esac
-               expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:"
-               remote=$(expr "z$ref" : 'z\([^:]*\):')
-               local=$(expr "z$ref" : 'z[^:]*:\(.*\)')
-               dot_prefix=.
-               if test -z "$merge_branches"
-               then
-                       merge_branches=$remote
-                       dot_prefix=
-               else
-                       for merge_branch in $merge_branches
-                       do
-                           [ "$remote" = "$merge_branch" ] &&
-                           dot_prefix= && break
-                       done
-               fi
-               case "$remote" in
-               '' | HEAD ) remote=HEAD ;;
-               refs/*) ;;
-               heads/* | tags/* | remotes/* ) remote="refs/$remote" ;;
-               *) remote="refs/heads/$remote" ;;
-               esac
-               case "$local" in
-               '') local= ;;
-               refs/*) ;;
-               heads/* | tags/* | remotes/* ) local="refs/$local" ;;
-               *) local="refs/heads/$local" ;;
-               esac
-
-               if local_ref_name=$(expr "z$local" : 'zrefs/\(.*\)')
-               then
-                  git check-ref-format "$local_ref_name" ||
-                  die "* refusing to create funny ref '$local_ref_name' locally"
-               fi
-               echo "${dot_prefix}${force}${remote}:${local}"
-       done
-}
-
-# Returns list of src: (no store), or src:dst (store)
-get_remote_default_refs_for_fetch () {
-       data_source=$(get_data_source "$1")
-       case "$data_source" in
-       '')
-               echo "HEAD:" ;;
-       self)
-               canon_refs_list_for_fetch -d "$1" \
-                       $(git for-each-ref --format='%(refname):')
-               ;;
-       config)
-               canon_refs_list_for_fetch -d "$1" \
-                       $(git config --get-all "remote.$1.fetch") ;;
-       branches)
-               remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1")
-               case "$remote_branch" in '') remote_branch=master ;; esac
-               echo "refs/heads/${remote_branch}:refs/heads/$1"
-               ;;
-       remotes)
-               canon_refs_list_for_fetch -d "$1" $(sed -ne '/^Pull: */{
-                                               s///p
-                                       }' "$GIT_DIR/remotes/$1")
-               ;;
-       *)
-               die "internal error: get-remote-default-ref-for-fetch $1" ;;
-       esac
-}
-
-get_remote_refs_for_push () {
+get_remote_merge_branch () {
        case "$#" in
-       0) die "internal error: get-remote-refs-for-push." ;;
-       1) get_remote_default_refs_for_push "$@" ;;
-       *) shift; echo "$@" ;;
-       esac
-}
-
-get_remote_refs_for_fetch () {
-       case "$#" in
-       0)
-           die "internal error: get-remote-refs-for-fetch." ;;
-       1)
-           get_remote_default_refs_for_fetch "$@" ;;
-       *)
-           shift
-           tag_just_seen=
-           for ref
-           do
-               if test "$tag_just_seen"
-               then
-                   echo "refs/tags/${ref}:refs/tags/${ref}"
-                   tag_just_seen=
-                   continue
-               else
-                   case "$ref" in
-                   tag)
-                       tag_just_seen=yes
-                       continue
-                       ;;
-                   esac
-               fi
-               canon_refs_list_for_fetch "$ref"
-           done
+       0|1)
+           origin="$1"
+           default=$(get_default_remote)
+           test -z "$origin" && origin=$default
+           curr_branch=$(git symbolic-ref -q HEAD)
+           [ "$origin" = "$default" ] &&
+           echo $(git for-each-ref --format='%(upstream)' $curr_branch)
            ;;
-       esac
-}
-
-resolve_alternates () {
-       # original URL (xxx.git)
-       top_=`expr "z$1" : 'z\([^:]*:/*[^/]*\)/'`
-       while read path
-       do
-               case "$path" in
-               \#* | '')
-                       continue ;;
-               /*)
-                       echo "$top_$path/" ;;
-               ../*)
-                       # relative -- ugly but seems to work.
-                       echo "$1/objects/$path/" ;;
-               *)
-                       # exit code may not be caught by the reader.
-                       echo "bad alternate: $path"
-                       exit 1 ;;
-               esac
-       done
-}
-
-get_uploadpack () {
-       data_source=$(get_data_source "$1")
-       case "$data_source" in
-       config)
-               uplp=$(git config --get "remote.$1.uploadpack")
-               echo ${uplp:-git-upload-pack}
-               ;;
        *)
-               echo "git-upload-pack"
+           repo=$1
+           shift
+           ref=$1
+           # FIXME: It should return the tracking branch
+           #        Currently only works with the default mapping
+           case "$ref" in
+           +*)
+               ref=$(expr "z$ref" : 'z+\(.*\)')
                ;;
+           esac
+           expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:"
+           remote=$(expr "z$ref" : 'z\([^:]*\):')
+           case "$remote" in
+           '' | HEAD ) remote=HEAD ;;
+           heads/*) remote=${remote#heads/} ;;
+           refs/heads/*) remote=${remote#refs/heads/} ;;
+           refs/* | tags/* | remotes/* ) remote=
+           esac
+
+           [ -n "$remote" ] && echo "refs/remotes/$repo/$remote"
        esac
 }
index 35261539ab80ffa46fef945dce1a82c5636c1b49..4b78a0cd37ba70236a05f78fb5bed3f763348096 100755 (executable)
@@ -125,12 +125,9 @@ test true = "$rebase" && {
        die "refusing to pull with rebase: your working tree is not up-to-date"
 
        . git-parse-remote &&
-       origin="$1"
-       test -z "$origin" && origin=$(get_default_remote)
-       reflist="$(get_remote_refs_for_fetch "$@" 2>/dev/null |
-               sed "s|refs/heads/\(.*\):|\1|")" &&
+       reflist="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
        oldremoteref="$(git rev-parse -q --verify \
-               "refs/remotes/$origin/$reflist")"
+               "$reflist")"
 }
 orig_head=$(git rev-parse -q --verify HEAD)
 git fetch $verbosity --update-head-ok "$@" || exit 1
@@ -176,13 +173,11 @@ case "$merge_head" in
 ?*' '?*)
        if test -z "$orig_head"
        then
-               echo >&2 "Cannot merge multiple branches into empty head"
-               exit 1
+               die "Cannot merge multiple branches into empty head"
        fi
        if test true = "$rebase"
        then
-               echo >&2 "Cannot rebase onto multiple branches"
-               exit 1
+               die "Cannot rebase onto multiple branches"
        fi
        ;;
 esac
index 314cd364b8f4df5e170dd0ffd9e874b3e6c2737c..f96d887d23653019e3387eced2779d50b3f09fa2 100755 (executable)
@@ -420,7 +420,7 @@ do_next () {
        NEWHEAD=$(git rev-parse HEAD) &&
        case $HEADNAME in
        refs/*)
-               message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
+               message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" &&
                git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
                git symbolic-ref HEAD $HEADNAME
                ;;
index b83fd3f970161d9ca8a4212a0b335f784fc3ff84..334629fc97c0d06e9c9cfda058bdd999810f9d55 100755 (executable)
@@ -168,10 +168,8 @@ run_pre_rebase_hook () {
        if test -z "$OK_TO_SKIP_PRE_REBASE" &&
           test -x "$GIT_DIR/hooks/pre-rebase"
        then
-               "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
-                       echo >&2 "The pre-rebase hook refused to rebase."
-                       exit 1
-               }
+               "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
+               die "The pre-rebase hook refused to rebase."
        fi
 }
 
@@ -359,8 +357,7 @@ fi
 
 # The tree must be really really clean.
 if ! git update-index --ignore-submodules --refresh; then
-       echo >&2 "cannot rebase: you have unstaged changes"
-       exit 1
+       die "cannot rebase: you have unstaged changes"
 fi
 diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
 case "$diff" in
index cccbf4517aa46d4d217a1ce25105f7c413301d2d..8ce6f1fe57579201c09cf960caed98d330ab2bbb 100755 (executable)
@@ -210,6 +210,7 @@ my %config_settings = (
     "envelopesender" => \$envelope_sender,
     "multiedit" => \$multiedit,
     "confirm"   => \$confirm,
+    "from" => \$sender,
 );
 
 # Handle Uncouth Termination
@@ -333,7 +334,7 @@ if (@suppress_cc) {
 }
 
 if ($suppress_cc{'all'}) {
-       foreach my $entry (qw (ccmd cc author self sob body bodycc)) {
+       foreach my $entry (qw (cccmd cc author self sob body bodycc)) {
                $suppress_cc{$entry} = 1;
        }
        delete $suppress_cc{'all'};
@@ -409,7 +410,7 @@ my %parse_alias = (
        mailrc => sub { my $fh = shift; while (<$fh>) {
                if (/^alias\s+(\S+)\s+(.*)$/) {
                        # spaces delimit multiple addresses
-                       $aliases{$1} = [ split(/\s+/, $2) ];
+                       $aliases{$1} = [ quotewords('\s+', 0, $2) ];
                }}},
        pine => sub { my $fh = shift; my $f='\t[^\t]*';
                for (my $x = ''; defined($x); $x = $_) {
@@ -537,7 +538,7 @@ if ($compose) {
 
        print C <<EOT;
 From $tpl_sender # This line is ignored.
-GIT: Lines beginning in "GIT: " will be removed.
+GIT: Lines beginning in "GIT:" will be removed.
 GIT: Consider including an overall diffstat or table of contents
 GIT: for the patch you are writing.
 GIT:
@@ -552,8 +553,6 @@ EOT
        }
        close(C);
 
-       my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-
        if ($annotate) {
                do_edit($compose_filename, @files);
        } else {
@@ -570,7 +569,7 @@ EOT
        my $in_body = 0;
        my $summary_empty = 1;
        while(<C>) {
-               next if m/^GIT: /;
+               next if m/^GIT:/;
                if ($in_body) {
                        $summary_empty = 0 unless (/^\n$/);
                } elsif (/^\n$/) {
@@ -578,7 +577,7 @@ EOT
                        if ($need_8bit_cte) {
                                print C2 "MIME-Version: 1.0\n",
                                         "Content-Type: text/plain; ",
-                                          "charset=utf-8\n",
+                                          "charset=UTF-8\n",
                                         "Content-Transfer-Encoding: 8bit\n";
                        }
                } elsif (/^MIME-Version:/i) {
@@ -767,12 +766,20 @@ sub unquote_rfc2047 {
 
 sub quote_rfc2047 {
        local $_ = shift;
-       my $encoding = shift || 'utf-8';
+       my $encoding = shift || 'UTF-8';
        s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
        s/(.*)/=\?$encoding\?q\?$1\?=/;
        return $_;
 }
 
+sub is_rfc2047_quoted {
+       my $s = shift;
+       my $token = '[^][()<>@,;:"\/?.= \000-\037\177-\377]+';
+       my $encoded_text = '[!->@-~]+';
+       length($s) <= 75 &&
+       $s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o;
+}
+
 # use the simplest quoting being able to handle the recipient
 sub sanitize_address
 {
@@ -784,7 +791,7 @@ sub sanitize_address
        }
 
        # if recipient_name is already quoted, do nothing
-       if ($recipient_name =~ /^("[[:ascii:]]*"|=\?utf-8\?q\?.*\?=)$/) {
+       if (is_rfc2047_quoted($recipient_name)) {
                return $recipient;
        }
 
@@ -804,6 +811,10 @@ sub sanitize_address
 
 }
 
+# Returns 1 if the message was sent, and 0 otherwise.
+# In actuality, the whole program dies when there
+# is an error sending a message.
+
 sub send_message
 {
        my @recipients = unique_email_list(@to);
@@ -872,7 +883,7 @@ X-Mailer: git-send-email $gitversion
                         default => $ask_default);
                die "Send this email reply required" unless defined $_;
                if (/^n/i) {
-                       return;
+                       return 0;
                } elsif (/^q/i) {
                        cleanup_compose_files();
                        exit(0);
@@ -953,7 +964,7 @@ X-Mailer: git-send-email $gitversion
                $smtp->data or die $smtp->message;
                $smtp->datasend("$header\n$message") or die $smtp->message;
                $smtp->dataend() or die $smtp->message;
-               $smtp->ok or die "Failed to send $subject\n".$smtp->message;
+               $smtp->code =~ /250|200/ or die "Failed to send $subject\n".$smtp->message;
        }
        if ($quiet) {
                printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject);
@@ -974,6 +985,8 @@ X-Mailer: git-send-email $gitversion
                        print "Result: OK\n";
                }
        }
+
+       return 1;
 }
 
 $reply_to = $initial_reply_to;
@@ -1091,7 +1104,7 @@ foreach my $t (@files) {
        close F;
 
        if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
-               open(F, "$cc_cmd $t |")
+               open(F, "$cc_cmd \Q$t\E |")
                        or die "(cc-cmd) Could not execute '$cc_cmd'";
                while(<F>) {
                        my $c = $_;
@@ -1134,10 +1147,11 @@ foreach my $t (@files) {
 
        @cc = (@initial_cc, @cc);
 
-       send_message();
+       my $message_was_sent = send_message();
 
        # set up for the next message
-       if ($chain_reply_to || !defined $reply_to || length($reply_to) == 0) {
+       if ($thread && $message_was_sent &&
+               ($chain_reply_to || !defined $reply_to || length($reply_to) == 0)) {
                $reply_to = $message_id;
                if (length $references > 0) {
                        $references .= "\n $message_id";
index 838233926f7ed07f781f2eb2e7547a2a5a086071..80acb7de729f2f824043b592ce110479d4334a06 100755 (executable)
 # exporting it.
 unset CDPATH
 
+git_broken_path_fix () {
+       case ":$PATH:" in
+       *:$1:*) : ok ;;
+       *)
+               PATH=$(
+                       SANE_TOOL_PATH="$1"
+                       IFS=: path= sep=
+                       set x $PATH
+                       shift
+                       for elem
+                       do
+                               case "$SANE_TOOL_PATH:$elem" in
+                               (?*:/bin | ?*:/usr/bin)
+                                       path="$path$sep$SANE_TOOL_PATH"
+                                       sep=:
+                                       SANE_TOOL_PATH=
+                               esac
+                               path="$path$sep$elem"
+                               sep=:
+                       done
+                       echo "$path"
+               )
+               ;;
+       esac
+}
+
+# @@BROKEN_PATH_FIX@@
+
 die() {
        echo >&2 "$@"
        exit 1
index b9ace9970492aaf48472904d978d809d90ca33db..e6a586720978ec516d243590b0e403ed7ecfe36c 100755 (executable)
@@ -3,8 +3,8 @@
 
 dashless=$(basename "$0" | sed -e 's/-/ /')
 USAGE="list [<options>]
-   or: $dashless (show | drop | pop ) [<stash>]
-   or: $dashless apply [--index] [<stash>]
+   or: $dashless ( show | drop ) [<stash>]
+   or: $dashless ( pop | apply ) [--index] [<stash>]
    or: $dashless branch <branchname> [<stash>]
    or: $dashless [save [--keep-index] [<message>]]
    or: $dashless clear"
index 8e234a4028d22e11baedba11f871d33f56945716..19a3a840fdc7eed4994741e13306b71a3ffd2ada 100755 (executable)
@@ -15,8 +15,10 @@ require_work_tree
 command=
 branch=
 quiet=
+reference=
 cached=
 nofetch=
+update=
 
 #
 # print stuff on stdout unless -q was specified
@@ -91,6 +93,7 @@ module_clone()
 {
        path=$1
        url=$2
+       reference="$3"
 
        # If there already is a directory at the submodule path,
        # expect it to be empty (since that is the default checkout
@@ -106,7 +109,12 @@ module_clone()
        test -e "$path" &&
        die "A file already exist at path '$path'"
 
-       git-clone -n "$url" "$path" ||
+       if test -n "$reference"
+       then
+               git-clone "$reference" -n "$url" "$path"
+       else
+               git-clone -n "$url" "$path"
+       fi ||
        die "Clone of '$url' into submodule path '$path' failed"
 }
 
@@ -131,6 +139,15 @@ cmd_add()
                -q|--quiet)
                        quiet=1
                        ;;
+               --reference)
+                       case "$2" in '') usage ;; esac
+                       reference="--reference=$2"
+                       shift
+                       ;;
+               --reference=*)
+                       reference="$1"
+                       shift
+                       ;;
                --)
                        shift
                        break
@@ -203,7 +220,7 @@ cmd_add()
                git config submodule."$path".url "$url"
        else
 
-               module_clone "$path" "$realrepo" || exit
+               module_clone "$path" "$realrepo" "$reference" || exit
                (
                        unset GIT_DIR
                        cd "$path" &&
@@ -294,6 +311,11 @@ cmd_init()
                git config submodule."$name".url "$url" ||
                die "Failed to register url for submodule path '$path'"
 
+               upd="$(git config -f .gitmodules submodule."$name".update)"
+               test -z "$upd" ||
+               git config submodule."$name".update "$upd" ||
+               die "Failed to register update mode for submodule path '$path'"
+
                say "Submodule '$name' ($url) registered for path '$path'"
        done
 }
@@ -314,13 +336,26 @@ cmd_update()
                        quiet=1
                        ;;
                -i|--init)
+                       init=1
                        shift
-                       cmd_init "$@" || return
                        ;;
                -N|--no-fetch)
                        shift
                        nofetch=1
                        ;;
+               -r|--rebase)
+                       shift
+                       update="rebase"
+                       ;;
+               --reference)
+                       case "$2" in '') usage ;; esac
+                       reference="--reference=$2"
+                       shift 2
+                       ;;
+               --reference=*)
+                       reference="$1"
+                       shift
+                       ;;
                --)
                        shift
                        break
@@ -334,11 +369,17 @@ cmd_update()
                esac
        done
 
+       if test -n "$init"
+       then
+               cmd_init "--" "$@" || return
+       fi
+
        module_list "$@" |
        while read mode sha1 stage path
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
+               update_module=$(git config submodule."$name".update)
                if test -z "$url"
                then
                        # Only mention uninitialized submodules when its
@@ -351,7 +392,7 @@ cmd_update()
 
                if ! test -d "$path"/.git -o -f "$path"/.git
                then
-                       module_clone "$path" "$url" || exit
+                       module_clone "$path" "$url" "$reference"|| exit
                        subsha1=
                else
                        subsha1=$(unset GIT_DIR; cd "$path" &&
@@ -359,6 +400,11 @@ cmd_update()
                        die "Unable to find current revision in submodule path '$path'"
                fi
 
+               if ! test -z "$update"
+               then
+                       update_module=$update
+               fi
+
                if test "$subsha1" != "$sha1"
                then
                        force=
@@ -374,11 +420,22 @@ cmd_update()
                                die "Unable to fetch in submodule path '$path'"
                        fi
 
-                       (unset GIT_DIR; cd "$path" &&
-                                 git-checkout $force -q "$sha1") ||
-                       die "Unable to checkout '$sha1' in submodule path '$path'"
+                       case "$update_module" in
+                       rebase)
+                               command="git rebase"
+                               action="rebase"
+                               msg="rebased onto"
+                               ;;
+                       *)
+                               command="git checkout $force -q"
+                               action="checkout"
+                               msg="checked out"
+                               ;;
+                       esac
 
-                       say "Submodule path '$path': checked out '$sha1'"
+                       (unset GIT_DIR; cd "$path" && $command "$sha1") ||
+                       die "Unable to $action '$sha1' in submodule path '$path'"
+                       say "Submodule path '$path': $msg '$sha1'"
                fi
        done
 }
index ef1d30db3889d0d33b43b1d15cd19281291f5aea..33017974d0a098bdaff707ccf0e8660aa5009079 100755 (executable)
@@ -5,7 +5,7 @@ use warnings;
 use strict;
 use vars qw/   $AUTHOR $VERSION
                $sha1 $sha1_short $_revision $_repository
-               $_q $_authors %users/;
+               $_q $_authors $_authors_prog %users/;
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
 $VERSION = '@@GIT_VERSION@@';
 
@@ -39,6 +39,7 @@ use Digest::MD5;
 use IO::File qw//;
 use File::Basename qw/dirname basename/;
 use File::Path qw/mkpath/;
+use File::Spec;
 use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
 use IPC::Open3;
 use Git;
@@ -76,6 +77,7 @@ my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex );
 my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
                'authors-file|A=s' => \$_authors,
+               'authors-prog=s' => \$_authors_prog,
                'repack:i' => \$Git::SVN::_repack,
                'noMetadata' => \$Git::SVN::_no_metadata,
                'useSvmProps' => \$Git::SVN::_use_svm_props,
@@ -263,6 +265,9 @@ usage(0) if $_help;
 version() if $_version;
 usage(1) unless defined $cmd;
 load_authors() if $_authors;
+if (defined $_authors_prog) {
+       $_authors_prog = "'" . File::Spec->rel2abs($_authors_prog) . "'";
+}
 
 unless ($cmd =~ /^(?:clone|init|multi-init|commit-diff)$/) {
        Git::SVN::Migration::migration_check();
@@ -361,6 +366,7 @@ sub cmd_clone {
        $path = basename($url) if !defined $path || !length $path;
        cmd_init($url, $path);
        Git::SVN::fetch_all($Git::SVN::default_repo_id);
+       command_oneline('config', 'svn.authorsfile', $_authors) if $_authors;
 }
 
 sub cmd_init {
@@ -1172,16 +1178,27 @@ sub get_commit_entry {
        }
        rename $commit_editmsg, $commit_msg or croak $!;
        {
+               require Encode;
                # SVN requires messages to be UTF-8 when entering the repo
                local $/;
                open $log_fh, '<', $commit_msg or croak $!;
                binmode $log_fh;
                chomp($log_entry{log} = <$log_fh>);
 
-               if (my $enc = Git::config('i18n.commitencoding')) {
-                       require Encode;
-                       Encode::from_to($log_entry{log}, $enc, 'UTF-8');
+               my $enc = Git::config('i18n.commitencoding') || 'UTF-8';
+               my $msg = $log_entry{log};
+
+               eval { $msg = Encode::decode($enc, $msg, 1) };
+               if ($@) {
+                       die "Could not decode as $enc:\n", $msg,
+                           "\nPerhaps you need to set i18n.commitencoding\n";
                }
+
+               eval { $msg = Encode::encode('UTF-8', $msg, 1) };
+               die "Could not encode as UTF-8:\n$msg\n" if $@;
+
+               $log_entry{log} = $msg;
+
                close $log_fh or croak $!;
        }
        unlink $commit_msg;
@@ -2663,12 +2680,33 @@ sub other_gs {
        $gs
 }
 
+sub call_authors_prog {
+       my ($orig_author) = @_;
+       my $author = `$::_authors_prog $orig_author`;
+       if ($? != 0) {
+               die "$::_authors_prog failed with exit code $?\n"
+       }
+       if ($author =~ /^\s*(.+?)\s*<(.*)>\s*$/) {
+               my ($name, $email) = ($1, $2);
+               $email = undef if length $2 == 0;
+               return [$name, $email];
+       } else {
+               die "Author: $orig_author: $::_authors_prog returned "
+                       . "invalid author format: $author\n";
+       }
+}
+
 sub check_author {
        my ($author) = @_;
        if (!defined $author || length $author == 0) {
                $author = '(no author)';
-       } elsif (defined $::_authors && ! defined $::users{$author}) {
-               die "Author: $author not defined in $::_authors file\n";
+       }
+       if (!defined $::users{$author}) {
+               if (defined $::_authors_prog) {
+                       $::users{$author} = call_authors_prog($author);
+               } elsif (defined $::_authors) {
+                       die "Author: $author not defined in $::_authors file\n";
+               }
        }
        $author;
 }
@@ -4438,6 +4476,7 @@ sub gs_fetch_loop_common {
        my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
        my $longest_path = longest_common_path($gsv, $globs);
        my $ra_url = $self->{url};
+       my $find_trailing_edge;
        while (1) {
                my %revs;
                my $err;
@@ -4455,8 +4494,10 @@ sub gs_fetch_loop_common {
                               sub { $revs{$_[1]} = _cb(@_) });
                if ($err) {
                        print "Checked through r$max\r";
+               } else {
+                       $find_trailing_edge = 1;
                }
-               if ($err && $max >= $head) {
+               if ($err and $find_trailing_edge) {
                        print STDERR "Path '$longest_path' ",
                                     "was probably deleted:\n",
                                     $err->expanded_message,
@@ -4468,13 +4509,14 @@ sub gs_fetch_loop_common {
                                my $ok;
                                $self->get_log([$longest_path], $min, $hi,
                                               0, 1, 1, sub {
-                                              $ok ||= $_[1];
+                                              $ok = $_[1];
                                               $revs{$_[1]} = _cb(@_) });
                                if ($ok) {
                                        print STDERR "r$min .. r$ok OK\n";
                                        last;
                                }
                        }
+                       $find_trailing_edge = 0;
                }
                $SVN::Error::handler = $err_handler;
 
index 7ed0faddcd75e024ce18d8b80994d41a9207a7ca..4f5c740df5651853e575ba68366152992b0f043d 100755 (executable)
@@ -161,9 +161,12 @@ case "$browser" in
                ;;
        esac
        ;;
-    w3m|links|lynx|open|start)
+    w3m|links|lynx|open)
        eval "$browser_path" "$@"
        ;;
+    start)
+        exec "$browser_path" '"web-browse"' "$@"
+        ;;
     dillo)
        "$browser_path" "$@" &
        ;;
diff --git a/git.c b/git.c
index 5a00726d09a13d5f70ab0378e02bbbd299124d5c..7d7f949f0da85ea124f67ead6f68fdd0ddc75d20 100644 (file)
--- a/git.c
+++ b/git.c
@@ -327,6 +327,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "merge-ours", cmd_merge_ours, RUN_SETUP },
                { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
                { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+               { "mktree", cmd_mktree, RUN_SETUP },
                { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
                { "name-rev", cmd_name_rev, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
index 1a7887b2528140d63b84a72843c0d0ef64f56538..4604c831fe0c5ac9b855cdef6f71888e99dd9462 100644 (file)
@@ -187,7 +187,8 @@ proc parseviewargs {n arglist} {
            "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
            "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
            "--remove-empty" - "--first-parent" - "--cherry-pick" -
-           "-S*" - "--pickaxe-all" - "--pickaxe-regex" {
+           "-S*" - "--pickaxe-all" - "--pickaxe-regex" -
+           "--simplify-by-decoration" {
                # These mean that we get a subset of the commits
                set filtered 1
                lappend glflags $arg
@@ -2145,7 +2146,7 @@ proc makewindow {} {
     label .bleft.mid.labeldiffcontext -text "      [mc "Lines of context"]: "
     pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
     spinbox .bleft.mid.diffcontext -width 5 -font textfont \
-       -from 1 -increment 1 -to 10000000 \
+       -from 0 -increment 1 -to 10000000 \
        -validate all -validatecommand "diffcontextvalidate %P" \
        -textvariable diffcontextstring
     .bleft.mid.diffcontext set $diffcontext
@@ -3671,17 +3672,36 @@ proc newview {ishighlight} {
 }
 
 set known_view_options {
-    {perm    b    . {}               {mc "Remember this view"}}
-    {args    t50= + {}               {mc "Commits to include (arguments to git log):"}}
-    {all     b    * "--all"          {mc "Use all refs"}}
-    {dorder  b    . {"--date-order" "-d"}      {mc "Strictly sort by date"}}
-    {lright  b    . "--left-right"   {mc "Mark branch sides"}}
-    {since   t15  + {"--since=*" "--after=*"}  {mc "Since date:"}}
-    {until   t15  . {"--until=*" "--before=*"} {mc "Until date:"}}
-    {limit   t10  + "--max-count=*"  {mc "Max count:"}}
-    {skip    t10  . "--skip=*"       {mc "Skip:"}}
-    {first   b    . "--first-parent" {mc "Limit to first parent"}}
-    {cmd     t50= + {}               {mc "Command to generate more commits to include:"}}
+    {perm      b    .  {}               {mc "Remember this view"}}
+    {reflabel  l    +  {}               {mc "References (space separated list):"}}
+    {refs      t15  .. {}               {mc "Branches & tags:"}}
+    {allrefs   b    *. "--all"          {mc "All refs"}}
+    {branches  b    .  "--branches"     {mc "All (local) branches"}}
+    {tags      b    .  "--tags"         {mc "All tags"}}
+    {remotes   b    .  "--remotes"      {mc "All remote-tracking branches"}}
+    {commitlbl l    +  {}               {mc "Commit Info (regular expressions):"}}
+    {author    t15  .. "--author=*"     {mc "Author:"}}
+    {committer t15  .  "--committer=*"  {mc "Committer:"}}
+    {loginfo   t15  .. "--grep=*"       {mc "Commit Message:"}}
+    {allmatch  b    .. "--all-match"    {mc "Matches all Commit Info criteria"}}
+    {changes_l l    +  {}               {mc "Changes to Files:"}}
+    {pickaxe_s r0   .  {}               {mc "Fixed String"}}
+    {pickaxe_t r1   .  "--pickaxe-regex"  {mc "Regular Expression"}}
+    {pickaxe   t15  .. "-S*"            {mc "Search string:"}}
+    {datelabel l    +  {}               {mc "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 15:27:38\"):"}}
+    {since     t15  ..  {"--since=*" "--after=*"}  {mc "Since:"}}
+    {until     t15  .   {"--until=*" "--before=*"} {mc "Until:"}}
+    {limit_lbl l    +  {}               {mc "Limit and/or skip a number of revisions (positive integer):"}}
+    {limit     t10  *. "--max-count=*"  {mc "Number to show:"}}
+    {skip      t10  .  "--skip=*"       {mc "Number to skip:"}}
+    {misc_lbl  l    +  {}               {mc "Miscellaneous options:"}}
+    {dorder    b    *. {"--date-order" "-d"}      {mc "Strictly sort by date"}}
+    {lright    b    .  "--left-right"   {mc "Mark branch sides"}}
+    {first     b    .  "--first-parent" {mc "Limit to first parent"}}
+    {smplhst   b    .  "--simplify-by-decoration"   {mc "Simple history"}}
+    {args      t50  *. {}               {mc "Additional arguments to git log:"}}
+    {allpaths  path +  {}               {mc "Enter files and directories to include, one per line:"}}
+    {cmd       t50= +  {}               {mc "Command to generate more commits to include:"}}
     }
 
 proc encode_view_opts {n} {
@@ -3693,13 +3713,19 @@ proc encode_view_opts {n} {
        if {$patterns eq {}} continue
        set pattern [lindex $patterns 0]
 
-       set val $newviewopts($n,[lindex $opt 0])
-       
        if {[lindex $opt 1] eq "b"} {
+           set val $newviewopts($n,[lindex $opt 0])
            if {$val} {
                lappend rargs $pattern
            }
+       } elseif {[regexp {^r(\d+)$} [lindex $opt 1] type value]} {
+           regexp {^(.*_)} [lindex $opt 0] uselessvar button_id
+           set val $newviewopts($n,$button_id)
+           if {$val eq $value} {
+               lappend rargs $pattern
+           }
        } else {
+           set val $newviewopts($n,[lindex $opt 0])
            set val [string trim $val]
            if {$val ne {}} {
                set pfix [string range $pattern 0 end-1]
@@ -3707,6 +3733,7 @@ proc encode_view_opts {n} {
            }
        }
     }
+    set rargs [concat $rargs [shellsplit $newviewopts($n,refs)]]
     return [concat $rargs [shellsplit $newviewopts($n,args)]]
 }
 
@@ -3714,14 +3741,22 @@ proc decode_view_opts {n view_args} {
     global known_view_options newviewopts
 
     foreach opt $known_view_options {
+       set id [lindex $opt 0]
        if {[lindex $opt 1] eq "b"} {
+           # Checkboxes
+           set val 0
+        } elseif {[regexp {^r(\d+)$} [lindex $opt 1]]} {
+           # Radiobuttons
+           regexp {^(.*_)} $id uselessvar id
            set val 0
        } else {
+           # Text fields
            set val {}
        }
-       set newviewopts($n,[lindex $opt 0]) $val
+       set newviewopts($n,$id) $val
     }
     set oargs [list]
+    set refargs [list]
     foreach arg $view_args {
        if {[regexp -- {^-([0-9]+)$} $arg arg cnt]
            && ![info exists found(limit)]} {
@@ -3735,11 +3770,17 @@ proc decode_view_opts {n view_args} {
            if {[info exists found($id)]} continue
            foreach pattern [lindex $opt 3] {
                if {![string match $pattern $arg]} continue
-               if {[lindex $opt 1] ne "b"} {
+               if {[lindex $opt 1] eq "b"} {
+                   # Check buttons
+                   set val 1
+               } elseif {[regexp {^r(\d+)$} [lindex $opt 1] match num]} {
+                   # Radio buttons
+                   regexp {^(.*_)} $id uselessvar id
+                   set val $num
+               } else {
+                   # Text input fields
                    set size [string length $pattern]
                    set val [string range $arg [expr {$size-1}] end]
-               } else {
-                   set val 1
                }
                set newviewopts($n,$id) $val
                set found($id) 1
@@ -3748,8 +3789,13 @@ proc decode_view_opts {n view_args} {
            if {[info exists val]} break
        }
        if {[info exists val]} continue
-       lappend oargs $arg
+       if {[regexp {^-} $arg]} {
+           lappend oargs $arg
+       } else {
+           lappend refargs $arg
+       }
     }
+    set newviewopts($n,refs) [shellarglist $refargs]
     set newviewopts($n,args) [shellarglist $oargs]
 }
 
@@ -3785,16 +3831,16 @@ proc vieweditor {top n title} {
     global known_view_options
 
     toplevel $top
-    wm title $top $title
+    wm title $top [concat $title [mc "-- criteria for selecting revisions"]]
     make_transient $top .
 
     # View name
     frame $top.nfr
-    label $top.nl -text [mc "Name"]
+    label $top.nl -text [mc "View Name:"]
     entry $top.name -width 20 -textvariable newviewname($n)
     pack $top.nfr -in $top -fill x -pady 5 -padx 3
-    pack $top.nl -in $top.nfr -side left -padx {0 30}
-    pack $top.name -in $top.nfr -side left
+    pack $top.nl -in $top.nfr -side left -padx {0 5}
+    pack $top.name -in $top.nfr -side left -padx {0 25}
 
     # View options
     set cframe $top.nfr
@@ -3813,14 +3859,28 @@ proc vieweditor {top n title} {
            frame $cframe
            pack $cframe -in $top -fill x -pady 3 -padx 3
            set cexpand [expr {$flags eq "*"}]
+        } elseif {$flags eq ".." || $flags eq "*."} {
+           set cframe $top.fr$cnt
+           incr cnt
+           frame $cframe
+           pack $cframe -in $top -fill x -pady 3 -padx [list 15 3]
+           set cexpand [expr {$flags eq "*."}]
        } else {
            set lxpad 5
        }
 
-       if {$type eq "b"} {
+       if {$type eq "l"} {
+            label $cframe.l_$id -text $title
+            pack $cframe.l_$id -in $cframe -side left -pady [list 3 0] -anchor w
+       } elseif {$type eq "b"} {
            checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
            pack $cframe.c_$id -in $cframe -side left \
                -padx [list $lxpad 0] -expand $cexpand -anchor w
+       } elseif {[regexp {^r(\d+)$} $type type sz]} {
+           regexp {^(.*_)} $id uselessvar button_id
+           radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
+           pack $cframe.c_$id -in $cframe -side left \
+               -padx [list $lxpad 0] -expand $cexpand -anchor w
        } elseif {[regexp {^t(\d+)$} $type type sz]} {
            message $cframe.l_$id -aspect 1500 -text $title
            entry $cframe.e_$id -width $sz -background $bgcolor \
@@ -3833,23 +3893,22 @@ proc vieweditor {top n title} {
                -textvariable newviewopts($n,$id)
            pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w
            pack $cframe.e_$id -in $cframe -side top -fill x
+       } elseif {$type eq "path"} {
+           message $top.l -aspect 1500 -text $title
+           pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3
+           text $top.t -width 40 -height 5 -background $bgcolor -font uifont
+           if {[info exists viewfiles($n)]} {
+               foreach f $viewfiles($n) {
+                   $top.t insert end $f
+                   $top.t insert end "\n"
+               }
+               $top.t delete {end - 1c} end
+               $top.t mark set insert 0.0
+           }
+           pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3
        }
     }
 
-    # Path list
-    message $top.l -aspect 1500 \
-       -text [mc "Enter files and directories to include, one per line:"]
-    pack $top.l -in $top -side top -pady [list 7 0] -anchor w -padx 3
-    text $top.t -width 40 -height 5 -background $bgcolor -font uifont
-    if {[info exists viewfiles($n)]} {
-       foreach f $viewfiles($n) {
-           $top.t insert end $f
-           $top.t insert end "\n"
-       }
-       $top.t delete {end - 1c} end
-       $top.t mark set insert 0.0
-    }
-    pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3
     frame $top.buts
     button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
     button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
@@ -7249,7 +7308,7 @@ proc diffcontextchange {n1 n2 op} {
     global diffcontextstring diffcontext
 
     if {[string is integer -strict $diffcontextstring]} {
-       if {$diffcontextstring > 0} {
+       if {$diffcontextstring >= 0} {
            set diffcontext $diffcontextstring
            reselectline
        }
@@ -7267,8 +7326,13 @@ proc getblobdiffs {ids} {
     global ignorespace
     global limitdiffs vfilelimit curview
     global diffencoding targetline diffnparents
+    global git_version
 
-    set cmd [diffcmd $ids "-p -C --cc --no-commit-id -U$diffcontext"]
+    set textconv {}
+    if {[package vcompare $git_version "1.6.1"] >= 0} {
+       set textconv "--textconv"
+    }
+    set cmd [diffcmd $ids "-p $textconv -C --cc --no-commit-id -U$diffcontext"]
     if {$ignorespace} {
        append cmd " -w"
     }
@@ -11090,6 +11154,7 @@ set nullid2 "0000000000000000000000000000000000000001"
 set nullfile "/dev/null"
 
 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
+set git_version [join [lrange [split [lindex [exec git version] end] .] 0 2] .]
 
 set runq {}
 set history {}
index 825dc98f74b97b577d74dacf61bfb4fe482ee2a8..53ef0d635941b54ddc9590f9df309536631114f8 100644 (file)
@@ -1,14 +1,15 @@
 # Translation of gitk to German.
 # Copyright (C) 2007 Paul Mackerras.
 # This file is distributed under the same license as the gitk package.
-# Christian Stimming <stimming@tuhh.de>, 2007
 #
+# Christian Stimming <stimming@tuhh.de>, 2007.
+# Frederik Schwarzer <schwarzerf@gmail.com>, 2008.
 msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-06 20:40+0100\n"
-"PO-Revision-Date: 2008-12-06 20:45+0100\n"
+"POT-Creation-Date: 2009-05-12 21:55+0200\n"
+"PO-Revision-Date: 2009-05-12 22:18+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
@@ -17,297 +18,314 @@ msgstr ""
 
 #: gitk:113
 msgid "Couldn't get list of unmerged files:"
-msgstr "Liste der nicht-zusammengeführten Dateien nicht gefunden:"
+msgstr "Liste der nicht zusammengeführten Dateien nicht gefunden:"
 
-#: gitk:272
+#: gitk:268
 msgid "Error parsing revisions:"
 msgstr "Fehler beim Laden der Versionen:"
 
-#: gitk:327
+#: gitk:323
 msgid "Error executing --argscmd command:"
-msgstr "Fehler beim --argscmd Kommando:"
+msgstr "Fehler beim Ausführen des --argscmd-Kommandos:"
 
-#: gitk:340
+#: gitk:336
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
-"Keine Dateien ausgewählt: --merge angegeben, es existieren aber keine nicht-"
-"zusammengeführten Dateien."
+"Keine Dateien ausgewählt: Es wurde --merge angegeben, aber es existieren "
+"keine nicht zusammengeführten Dateien."
 
-#: gitk:343
+#: gitk:339
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
 msgstr ""
-"Keine Dateien ausgewähle: --merge angegeben, aber keine nicht-"
-"zusammengeführten Dateien sind in der Dateiauswahl."
+"Keine Dateien ausgewählt: Es wurde --merge angegeben, aber es sind keine "
+"nicht zusammengeführten Dateien in der Dateiauswahl."
 
-#: gitk:365 gitk:503
+#: gitk:361 gitk:508
 msgid "Error executing git log:"
-msgstr "Fehler beim Ausführen von git-log:"
+msgstr "Fehler beim Ausführen von »git log«:"
 
-#: gitk:378
+#: gitk:379 gitk:524
 msgid "Reading"
 msgstr "Lesen"
 
-#: gitk:438 gitk:3462
+#: gitk:439 gitk:4061
 msgid "Reading commits..."
-msgstr "Versionen lesen..."
+msgstr "Versionen werden gelesen ..."
 
-#: gitk:441 gitk:1528 gitk:3465
+#: gitk:442 gitk:1560 gitk:4064
 msgid "No commits selected"
-msgstr "Keine Versionen ausgewählt."
+msgstr "Keine Versionen ausgewählt"
 
-#: gitk:1399
+#: gitk:1436
 msgid "Can't parse git log output:"
-msgstr "Ausgabe von git-log kann nicht erkannt werden:"
+msgstr "Ausgabe von »git log« kann nicht erkannt werden:"
 
-#: gitk:1605
+#: gitk:1656
 msgid "No commit information available"
 msgstr "Keine Versionsinformation verfügbar"
 
-#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
+#: gitk:1791 gitk:1815 gitk:3854 gitk:8714 gitk:10250 gitk:10422
 msgid "OK"
 msgstr "Ok"
 
-#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
-#: gitk:9294 gitk:9467
+#: gitk:1817 gitk:3856 gitk:8311 gitk:8385 gitk:8495 gitk:8544 gitk:8716
+#: gitk:10251 gitk:10423
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: gitk:1811
+#: gitk:1917
 msgid "Update"
 msgstr "Aktualisieren"
 
-#: gitk:1812
+#: gitk:1918
 msgid "Reload"
 msgstr "Neu laden"
 
-#: gitk:1813
+#: gitk:1919
 msgid "Reread references"
 msgstr "Zweige neu laden"
 
-#: gitk:1814
+#: gitk:1920
 msgid "List references"
 msgstr "Zweige/Markierungen auflisten"
 
-#: gitk:1915
+#: gitk:1922
 msgid "Start git gui"
 msgstr "»git gui« starten"
 
-#: gitk:1917
+#: gitk:1924
 msgid "Quit"
 msgstr "Beenden"
 
-#: gitk:1810
+#: gitk:1916
 msgid "File"
 msgstr "Datei"
 
-#: gitk:1818
+#: gitk:1928
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: gitk:1817
+#: gitk:1927
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: gitk:1821
+#: gitk:1932
 msgid "New view..."
-msgstr "Neue Ansicht..."
+msgstr "Neue Ansicht ..."
 
-#: gitk:1822
+#: gitk:1933
 msgid "Edit view..."
-msgstr "Ansicht bearbeiten..."
+msgstr "Ansicht bearbeiten ..."
 
-#: gitk:1823
+#: gitk:1934
 msgid "Delete view"
-msgstr "Ansicht löschen"
+msgstr "Ansicht entfernen"
 
-#: gitk:1825
+#: gitk:1936
 msgid "All files"
 msgstr "Alle Dateien"
 
-#: gitk:1820 gitk:3196
+#: gitk:1931 gitk:3666
 msgid "View"
 msgstr "Ansicht"
 
-#: gitk:1828 gitk:2487
+#: gitk:1941 gitk:1951 gitk:2650
 msgid "About gitk"
 msgstr "Über gitk"
 
-#: gitk:1829
+#: gitk:1942 gitk:1956
 msgid "Key bindings"
 msgstr "Tastenkürzel"
 
-#: gitk:1827
+#: gitk:1940 gitk:1955
 msgid "Help"
 msgstr "Hilfe"
 
-#: gitk:1887
+#: gitk:2016
 msgid "SHA1 ID: "
 msgstr "SHA1:"
 
-#: gitk:1918
+#: gitk:2047
 msgid "Row"
 msgstr "Zeile"
 
-#: gitk:1949
+#: gitk:2078
 msgid "Find"
 msgstr "Suche"
 
-#: gitk:1950
+#: gitk:2079
 msgid "next"
 msgstr "nächste"
 
-#: gitk:1951
+#: gitk:2080
 msgid "prev"
 msgstr "vorige"
 
-#: gitk:1952
+#: gitk:2081
 msgid "commit"
 msgstr "Version nach"
 
-#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
+#: gitk:2084 gitk:2086 gitk:4222 gitk:4245 gitk:4269 gitk:6210 gitk:6282
+#: gitk:6366
 msgid "containing:"
 msgstr "Beschreibung:"
 
-#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
+#: gitk:2087 gitk:3158 gitk:3163 gitk:4297
 msgid "touching paths:"
 msgstr "Dateien:"
 
-#: gitk:1959 gitk:3697
+#: gitk:2088 gitk:4302
 msgid "adding/removing string:"
 msgstr "Änderungen:"
 
-#: gitk:1968 gitk:1970
+#: gitk:2097 gitk:2099
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:1970 gitk:3773 gitk:5518
+#: gitk:2099 gitk:4377 gitk:6178
 msgid "IgnCase"
 msgstr "Kein Groß/Klein"
 
-#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
+#: gitk:2099 gitk:4271 gitk:4375 gitk:6174
 msgid "Regexp"
 msgstr "Regexp"
 
-#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
+#: gitk:2101 gitk:2102 gitk:4396 gitk:4426 gitk:4433 gitk:6302 gitk:6370
 msgid "All fields"
 msgstr "Alle Felder"
 
-#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
+#: gitk:2102 gitk:4394 gitk:4426 gitk:6241
 msgid "Headline"
 msgstr "Überschrift"
 
-#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
+#: gitk:2103 gitk:4394 gitk:6241 gitk:6370 gitk:6804
 msgid "Comments"
 msgstr "Beschreibung"
 
-#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
-#: gitk:7300
+#: gitk:2103 gitk:4394 gitk:4398 gitk:4433 gitk:6241 gitk:6739 gitk:7991
+#: gitk:8006
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
+#: gitk:2103 gitk:4394 gitk:6241 gitk:6741
 msgid "Committer"
 msgstr "Eintragender"
 
-#: gitk:2003
+#: gitk:2132
 msgid "Search"
-msgstr "Suche"
+msgstr "Suchen"
 
-#: gitk:2010
+#: gitk:2139
 msgid "Diff"
 msgstr "Vergleich"
 
-#: gitk:2012
+#: gitk:2141
 msgid "Old version"
 msgstr "Alte Version"
 
-#: gitk:2014
+#: gitk:2143
 msgid "New version"
 msgstr "Neue Version"
 
-#: gitk:2016
+#: gitk:2145
 msgid "Lines of context"
 msgstr "Kontextzeilen"
 
-#: gitk:2026
+#: gitk:2155
 msgid "Ignore space change"
 msgstr "Leerzeichenänderungen ignorieren"
 
-#: gitk:2084
+#: gitk:2213
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:2086
+#: gitk:2215
 msgid "Tree"
 msgstr "Baum"
 
-#: gitk:2213 gitk:2226
+#: gitk:2359 gitk:2376
 msgid "Diff this -> selected"
-msgstr "Vergleich diese -> gewählte"
+msgstr "Vergleich: diese -> gewählte"
 
-#: gitk:2214 gitk:2227
+#: gitk:2360 gitk:2377
 msgid "Diff selected -> this"
-msgstr "Vergleich gewählte -> diese"
+msgstr "Vergleich: gewählte -> diese"
 
-#: gitk:2215 gitk:2228
+#: gitk:2361 gitk:2378
 msgid "Make patch"
 msgstr "Patch erstellen"
 
-#: gitk:2216 gitk:7494
+#: gitk:2362 gitk:8369
 msgid "Create tag"
 msgstr "Markierung erstellen"
 
-#: gitk:2217 gitk:7593
+#: gitk:2363 gitk:8475
 msgid "Write commit to file"
 msgstr "Version in Datei schreiben"
 
-#: gitk:2218 gitk:7647
+#: gitk:2364 gitk:8532
 msgid "Create new branch"
 msgstr "Neuen Zweig erstellen"
 
-#: gitk:2219
+#: gitk:2365
 msgid "Cherry-pick this commit"
 msgstr "Diese Version pflücken"
 
-#: gitk:2220
+#: gitk:2366
 msgid "Reset HEAD branch to here"
 msgstr "HEAD-Zweig auf diese Version zurücksetzen"
 
-#: gitk:2234
+#: gitk:2367
+msgid "Mark this commit"
+msgstr "Lesezeichen setzen"
+
+#: gitk:2368
+msgid "Return to mark"
+msgstr "Zum Lesezeichen"
+
+#: gitk:2369
+msgid "Find descendant of this and mark"
+msgstr "Abkömmling von Lesezeichen und dieser Version finden"
+
+#: gitk:2370
+msgid "Compare with marked commit"
+msgstr "Mit Lesezeichen vergleichen"
+
+#: gitk:2384
 msgid "Check out this branch"
 msgstr "Auf diesen Zweig umstellen"
 
-#: gitk:2235
+#: gitk:2385
 msgid "Remove this branch"
 msgstr "Zweig löschen"
 
-#: gitk:2242
+#: gitk:2392
 msgid "Highlight this too"
 msgstr "Diesen auch hervorheben"
 
-#: gitk:2243
+#: gitk:2393
 msgid "Highlight this only"
 msgstr "Nur diesen hervorheben"
 
-#: gitk:2244
+#: gitk:2394
 msgid "External diff"
-msgstr "Externer Vergleich"
+msgstr "Externes Diff-Programm"
 
-#: gitk:2255
+#: gitk:2395
 msgid "Blame parent commit"
 msgstr "Annotieren der Elternversion"
 
-#: gitk:2360
+#: gitk:2402
 msgid "Show origin of this line"
 msgstr "Herkunft dieser Zeile anzeigen"
 
-#: gitk:2361
+#: gitk:2403
 msgid "Run git gui blame on this line"
-msgstr "Annotieren (»git gui blame«) von dieser Zeile"
+msgstr "Diese Zeile annotieren (»git gui blame«)"
 
-#: gitk:2606
+#: gitk:2652
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -317,504 +335,552 @@ msgid ""
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
-"Gitk - eine Visualisierung der Git Historie\n"
+"Gitk - eine Visualisierung der Git-Historie\n"
 "\n"
 "Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
 "Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public "
 "License"
 
-#: gitk:2496 gitk:2557 gitk:7943
+#: gitk:2660 gitk:2722 gitk:8897
 msgid "Close"
 msgstr "Schließen"
 
-#: gitk:2515
+#: gitk:2679
 msgid "Gitk key bindings"
-msgstr "Gitk Tastaturbelegung"
+msgstr "Gitk-Tastaturbelegung"
 
-#: gitk:2517
+#: gitk:2682
 msgid "Gitk key bindings:"
-msgstr "Gitk Tastaturbelegung:"
+msgstr "Gitk-Tastaturbelegung:"
 
-#: gitk:2519
+#: gitk:2684
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tBeenden"
 
-#: gitk:2520
+#: gitk:2685
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Pos1>\t\tZur neuesten Version springen"
 
-#: gitk:2521
+#: gitk:2686
 msgid "<End>\t\tMove to last commit"
 msgstr "<Ende>\t\tZur ältesten Version springen"
 
-#: gitk:2522
+#: gitk:2687
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Hoch>, p, i\tNächste neuere Version"
 
-#: gitk:2523
+#: gitk:2688
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Runter>, n, k\tNächste ältere Version"
 
-#: gitk:2524
+#: gitk:2689
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Links>, z, j\tEine Version zurückgehen"
 
-#: gitk:2525
+#: gitk:2690
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Rechts>, x, l\tEine Version weitergehen"
 
-#: gitk:2526
+#: gitk:2691
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<BildHoch>\tEine Seite nach oben blättern"
 
-#: gitk:2527
+#: gitk:2692
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<BildRunter>\tEine Seite nach unten blättern"
 
-#: gitk:2528
+#: gitk:2693
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Pos1>\tZum oberen Ende der Versionsliste blättern"
 
-#: gitk:2529
+#: gitk:2694
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-Ende>\tZum unteren Ende der Versionsliste blättern"
 
-#: gitk:2530
+#: gitk:2695
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Hoch>\tVersionsliste eine Zeile nach oben blättern"
 
-#: gitk:2531
+#: gitk:2696
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Runter>\tVersionsliste eine Zeile nach unten blättern"
 
-#: gitk:2532
+#: gitk:2697
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
-msgstr "<%s-BildHoch>\tVersionsliste eine Seite hoch blättern"
+msgstr "<%s-BildHoch>\tVersionsliste eine Seite nach oben blättern"
 
-#: gitk:2533
+#: gitk:2698
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-BildRunter>\tVersionsliste eine Seite nach unten blättern"
 
-#: gitk:2534
+#: gitk:2699
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Umschalt-Hoch>\tRückwärts suchen (nach oben; neuere Versionen)"
 
-#: gitk:2535
+#: gitk:2700
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Umschalt-Runter> Suchen (nach unten; ältere Versionen)"
 
-#: gitk:2536
+#: gitk:2701
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Entf>, b\t\tVergleich eine Seite nach oben blättern"
 
-#: gitk:2537
+#: gitk:2702
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Löschtaste>\tVergleich eine Seite nach oben blättern"
 
-#: gitk:2538
+#: gitk:2703
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Leertaste>\tVergleich eine Seite nach unten blättern"
 
-#: gitk:2539
+#: gitk:2704
 msgid "u\t\tScroll diff view up 18 lines"
-msgstr "u\t\tVergleich um 18 Zeilen nach oben (»up«) blättern"
+msgstr "u\t\tVergleich um 18 Zeilen nach oben blättern"
 
-#: gitk:2540
+#: gitk:2705
 msgid "d\t\tScroll diff view down 18 lines"
-msgstr "d\t\tVergleich um 18 Zeilen nach unten (»down«) blättern"
+msgstr "d\t\tVergleich um 18 Zeilen nach unten blättern"
 
-#: gitk:2541
+#: gitk:2706
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSuchen"
 
-#: gitk:2542
+#: gitk:2707
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tWeitersuchen"
 
-#: gitk:2543
+#: gitk:2708
 msgid "<Return>\tMove to next find hit"
 msgstr "<Eingabetaste>\tWeitersuchen"
 
-#: gitk:2544
-msgid "/\t\tMove to next find hit, or redo find"
-msgstr "/\t\tWeitersuchen oder neue Suche beginnen"
+#: gitk:2709
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tTastaturfokus ins Suchfeld"
 
-#: gitk:2545
+#: gitk:2710
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tRückwärts weitersuchen"
 
-#: gitk:2546
+#: gitk:2711
 msgid "f\t\tScroll diff view to next file"
-msgstr "f\t\tVergleich zur nächsten Datei (»file«) blättern"
+msgstr "f\t\tVergleich zur nächsten Datei blättern"
 
-#: gitk:2547
+#: gitk:2712
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tWeitersuchen im Vergleich"
 
-#: gitk:2548
+#: gitk:2713
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tRückwärts weitersuchen im Vergleich"
 
-#: gitk:2549
+#: gitk:2714
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
-msgstr "<%s-Nummerblock-Plus>\tSchriftgröße vergrößern"
+msgstr "<%s-Nummerblock-Plus>\tSchrift vergrößern"
 
-#: gitk:2550
+#: gitk:2715
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
-msgstr "<%s-Plus>\tSchriftgröße vergrößern"
+msgstr "<%s-Plus>\tSchrift vergrößern"
 
-#: gitk:2551
+#: gitk:2716
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
-msgstr "<%s-Nummernblock-> Schriftgröße verkleinern"
+msgstr "<%s-Nummernblock-Minus> Schrift verkleinern"
 
-#: gitk:2552
+#: gitk:2717
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
-msgstr "<%s-Minus>\tSchriftgröße verkleinern"
+msgstr "<%s-Minus>\tSchrift verkleinern"
 
-#: gitk:2553
+#: gitk:2718
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tAktualisieren"
 
-#: gitk:2979
+#: gitk:3173
 #, tcl-format
 msgid "Error getting \"%s\" from %s:"
 msgstr "Fehler beim Holen von »%s« von »%s«:"
 
-#: gitk:3036 gitk:3045
+#: gitk:3230 gitk:3239
 #, tcl-format
 msgid "Error creating temporary directory %s:"
-msgstr "Fehler beim Erzeugen eines temporären Verzeichnisses »%s«:"
+msgstr "Fehler beim Erzeugen des temporären Verzeichnisses »%s«:"
 
-#: gitk:3058
+#: gitk:3251
 msgid "command failed:"
 msgstr "Kommando fehlgeschlagen:"
 
-#: gitk:3078
+#: gitk:3397
 msgid "No such commit"
 msgstr "Version nicht gefunden"
 
-#: gitk:3083
+#: gitk:3411
 msgid "git gui blame: command failed:"
 msgstr "git gui blame: Kommando fehlgeschlagen:"
 
-#: gitk:3398
+#: gitk:3442
 #, tcl-format
 msgid "Couldn't read merge head: %s"
 msgstr "Zusammenführungs-Spitze konnte nicht gelesen werden: %s"
 
-#: gitk:3406
+#: gitk:3450
 #, tcl-format
 msgid "Error reading index: %s"
 msgstr "Fehler beim Lesen der Bereitstellung (»index«): %s"
 
-#: gitk:3431
+#: gitk:3475
 #, tcl-format
 msgid "Couldn't start git blame: %s"
 msgstr "»git blame« konnte nicht gestartet werden: %s"
 
-#: gitk:3434 gitk:6160
+#: gitk:3478 gitk:6209
 msgid "Searching"
 msgstr "Suchen"
 
-#: gitk:3466
+#: gitk:3510
 #, tcl-format
 msgid "Error running git blame: %s"
 msgstr "Fehler beim Ausführen von »git blame«: %s"
 
-#: gitk:3494
+#: gitk:3538
 #, tcl-format
 msgid "That line comes from commit %s,  which is not in this view"
 msgstr ""
-"Diese Zeile stammt aus Version %s, welche nicht in dieser Ansicht gezeigt "
-"wird."
+"Diese Zeile stammt aus Version %s, die nicht in dieser Ansicht gezeigt wird"
 
-#: gitk:3508
+#: gitk:3552
 msgid "External diff viewer failed:"
-msgstr "Externes Vergleich-(Diff-)Programm fehlgeschlagen:"
+msgstr "Externes Diff-Programm fehlgeschlagen:"
 
-#: gitk:3210
+#: gitk:3670
 msgid "Gitk view definition"
-msgstr "Gitk Ansichten"
+msgstr "Gitk-Ansichten"
 
-#: gitk:3630
+#: gitk:3674
 msgid "Remember this view"
 msgstr "Diese Ansicht speichern"
 
-#: gitk:3232
+#: gitk:3675
 msgid "Commits to include (arguments to git log):"
-msgstr "Versionen anzeigen (Argumente von git-log):"
+msgstr "Versionen anzeigen (Argumente von git log):"
 
-#: gitk:3632
+#: gitk:3676
 msgid "Use all refs"
 msgstr "Alle Zweige verwenden"
 
-#: gitk:3633
+#: gitk:3677
 msgid "Strictly sort by date"
 msgstr "Streng nach Datum sortieren"
 
-#: gitk:3634
+#: gitk:3678
 msgid "Mark branch sides"
 msgstr "Zweig-Seiten markieren"
 
-#: gitk:3635
+#: gitk:3679
 msgid "Since date:"
 msgstr "Von Datum:"
 
-#: gitk:3636
+#: gitk:3680
 msgid "Until date:"
 msgstr "Bis Datum:"
 
-#: gitk:3637
+#: gitk:3681
 msgid "Max count:"
 msgstr "Max. Anzahl:"
 
-#: gitk:3638
+#: gitk:3682
 msgid "Skip:"
 msgstr "Überspringen:"
 
-#: gitk:3639
+#: gitk:3683
 msgid "Limit to first parent"
 msgstr "Auf erste Elternversion beschränken"
 
-#: gitk:3640
+#: gitk:3684
 msgid "Command to generate more commits to include:"
 msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
 
-#: gitk:3749
+#: gitk:3780
+msgid "Gitk: edit view"
+msgstr "Gitk: Ansicht bearbeiten"
+
+#: gitk:3793
 msgid "Name"
 msgstr "Name"
 
-#: gitk:3797
+#: gitk:3841
 msgid "Enter files and directories to include, one per line:"
 msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):"
 
-#: gitk:3811
+#: gitk:3855
 msgid "Apply (F5)"
 msgstr "Anwenden (F5)"
 
-#: gitk:3849
+#: gitk:3893
 msgid "Error in commit selection arguments:"
 msgstr "Fehler in den ausgewählten Versionen:"
 
-#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
+#: gitk:3946 gitk:3998 gitk:4446 gitk:4460 gitk:5721 gitk:11114 gitk:11115
 msgid "None"
 msgstr "Keine"
 
-#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
+#: gitk:4394 gitk:6241 gitk:7993 gitk:8008
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:3790 gitk:5580
+#: gitk:4394 gitk:6241
 msgid "CDate"
 msgstr "Eintragedatum"
 
-#: gitk:3939 gitk:3944
+#: gitk:4543 gitk:4548
 msgid "Descendant"
 msgstr "Abkömmling"
 
-#: gitk:3940
+#: gitk:4544
 msgid "Not descendant"
-msgstr "Nicht Abkömmling"
+msgstr "Kein Abkömmling"
 
-#: gitk:3947 gitk:3952
+#: gitk:4551 gitk:4556
 msgid "Ancestor"
 msgstr "Vorgänger"
 
-#: gitk:3948
+#: gitk:4552
 msgid "Not ancestor"
-msgstr "Nicht Vorgänger"
+msgstr "Kein Vorgänger"
 
-#: gitk:4187
+#: gitk:4842
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen"
 
-#: gitk:4220
+#: gitk:4878
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokale Änderungen, nicht bereitgestellt"
 
-#: gitk:6673
+#: gitk:6559
+msgid "many"
+msgstr "viele"
+
+#: gitk:6743
 msgid "Tags:"
 msgstr "Markierungen:"
 
-#: gitk:6066 gitk:6072 gitk:7280
+#: gitk:6760 gitk:6766 gitk:7986
 msgid "Parent"
 msgstr "Eltern"
 
-#: gitk:6077
+#: gitk:6771
 msgid "Child"
 msgstr "Kind"
 
-#: gitk:6086
+#: gitk:6780
 msgid "Branch"
 msgstr "Zweig"
 
-#: gitk:6089
+#: gitk:6783
 msgid "Follows"
 msgstr "Folgt auf"
 
-#: gitk:6092
+#: gitk:6786
 msgid "Precedes"
 msgstr "Vorgänger von"
 
-#: gitk:7209
+#: gitk:7279
 #, tcl-format
 msgid "Error getting diffs: %s"
 msgstr "Fehler beim Laden des Vergleichs: %s"
 
-#: gitk:7748
+#: gitk:7819
 msgid "Goto:"
 msgstr "Gehe zu:"
 
-#: gitk:7115
+#: gitk:7821
 msgid "SHA1 ID:"
 msgstr "SHA1-Hashwert:"
 
-#: gitk:7134
+#: gitk:7840
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Kurzer SHA1-Hashwert »%s« ist mehrdeutig"
 
-#: gitk:7146
+#: gitk:7852
 #, tcl-format
 msgid "SHA1 id %s is not known"
-msgstr "SHA1-Hashwert »%s« unbekannt"
+msgstr "SHA1-Hashwert »%s« ist unbekannt"
 
-#: gitk:7148
+#: gitk:7854
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "Markierung/Zweig »%s« ist unbekannt"
 
-#: gitk:7290
+#: gitk:7996
 msgid "Children"
 msgstr "Kinder"
 
-#: gitk:7347
+#: gitk:8053
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Zweig »%s« hierher zurücksetzen"
 
-#: gitk:7349
+#: gitk:8055
 msgid "Detached head: can't reset"
 msgstr "Zweigspitze ist abgetrennt: Zurücksetzen nicht möglich"
 
-#: gitk:7381
+#: gitk:8164 gitk:8170
+msgid "Skipping merge commit "
+msgstr "Überspringe Zusammenführungs-Version "
+
+#: gitk:8179 gitk:8184
+msgid "Error getting patch ID for "
+msgstr "Fehler beim Holen der Patch-ID für "
+
+#: gitk:8180 gitk:8185
+msgid " - stopping\n"
+msgstr " - Abbruch.\n"
+
+#: gitk:8190 gitk:8193 gitk:8201 gitk:8211 gitk:8220
+msgid "Commit "
+msgstr "Version "
+
+#: gitk:8194
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" ist das gleiche Patch wie\n"
+"       "
+
+#: gitk:8202
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" ist unterschiedlich von\n"
+"       "
+
+#: gitk:8204
+msgid "- stopping\n"
+msgstr "- Abbruch.\n"
+
+#: gitk:8212 gitk:8221
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " hat %s Kinder. Abbruch\n"
+
+#: gitk:8252
 msgid "Top"
 msgstr "Oben"
 
-#: gitk:7382
+#: gitk:8253
 msgid "From"
 msgstr "Von"
 
-#: gitk:7387
+#: gitk:8258
 msgid "To"
 msgstr "bis"
 
-#: gitk:7410
+#: gitk:8282
 msgid "Generate patch"
 msgstr "Patch erstellen"
 
-#: gitk:7412
+#: gitk:8284
 msgid "From:"
 msgstr "Von:"
 
-#: gitk:7421
+#: gitk:8293
 msgid "To:"
 msgstr "bis:"
 
-#: gitk:7430
+#: gitk:8302
 msgid "Reverse"
 msgstr "Umgekehrt"
 
-#: gitk:7432 gitk:7607
+#: gitk:8304 gitk:8489
 msgid "Output file:"
 msgstr "Ausgabedatei:"
 
-#: gitk:7438
+#: gitk:8310
 msgid "Generate"
 msgstr "Erzeugen"
 
-#: gitk:7474
+#: gitk:8348
 msgid "Error creating patch:"
-msgstr "Fehler beim Patch erzeugen:"
+msgstr "Fehler beim Erzeugen des Patches:"
 
-#: gitk:7496 gitk:7595 gitk:7649
+#: gitk:8371 gitk:8477 gitk:8534
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:7505
+#: gitk:8380
 msgid "Tag name:"
 msgstr "Markierungsname:"
 
-#: gitk:7509 gitk:7659
+#: gitk:8384 gitk:8543
 msgid "Create"
 msgstr "Erstellen"
 
-#: gitk:7524
+#: gitk:8401
 msgid "No tag name specified"
 msgstr "Kein Markierungsname angegeben"
 
-#: gitk:7528
+#: gitk:8405
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Markierung »%s« existiert bereits."
 
-#: gitk:7534
+#: gitk:8411
 msgid "Error creating tag:"
-msgstr "Fehler bei Markierung erstellen:"
+msgstr "Fehler beim Erstellen der Markierung:"
 
-#: gitk:7604
+#: gitk:8486
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:7612
+#: gitk:8494
 msgid "Write"
 msgstr "Schreiben"
 
-#: gitk:7628
+#: gitk:8512
 msgid "Error writing commit:"
 msgstr "Fehler beim Schreiben der Version:"
 
-#: gitk:7654
+#: gitk:8539
 msgid "Name:"
 msgstr "Name:"
 
-#: gitk:7674
+#: gitk:8562
 msgid "Please specify a name for the new branch"
 msgstr "Bitte geben Sie einen Namen für den neuen Zweig an."
 
-#: gitk:8328
+#: gitk:8567
 #, tcl-format
 msgid "Branch '%s' already exists. Overwrite?"
 msgstr "Zweig »%s« existiert bereits. Soll er überschrieben werden?"
 
-#: gitk:8394
+#: gitk:8633
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut "
 "eintragen?"
 
-#: gitk:7718
+#: gitk:8638
 msgid "Cherry-picking"
 msgstr "Version pflücken"
 
-#: gitk:8408
+#: gitk:8647
 #, tcl-format
 msgid ""
 "Cherry-pick failed because of local changes to file '%s'.\n"
@@ -822,45 +888,45 @@ msgid ""
 msgstr ""
 "Pflücken fehlgeschlagen, da noch lokale Änderungen in Datei »%s«\n"
 "vorliegen. Bitte diese Änderungen eintragen, zurücksetzen oder\n"
-"zwischenspeichern (»git stash») und dann erneut versuchen."
+"zwischenspeichern (»git stash«) und dann erneut versuchen."
 
-#: gitk:8414
+#: gitk:8653
 msgid ""
 "Cherry-pick failed because of merge conflict.\n"
 "Do you wish to run git citool to resolve it?"
 msgstr ""
 "Pflücken fehlgeschlagen, da ein Zusammenführungs-Konflikt aufgetreten\n"
-"ist. Soll das »git citool« (Zusammenführungs-Werkzeug) aufgerufen\n"
+"ist. Soll das Zusammenführungs-Werkzeug (»git citool«) aufgerufen\n"
 "werden, um diesen Konflikt aufzulösen?"
 
-#: gitk:8430
+#: gitk:8669
 msgid "No changes committed"
 msgstr "Keine Änderungen eingetragen"
 
-#: gitk:7745
+#: gitk:8695
 msgid "Confirm reset"
 msgstr "Zurücksetzen bestätigen"
 
-#: gitk:7747
+#: gitk:8697
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Zweig »%s« auf »%s« zurücksetzen?"
 
-#: gitk:7751
+#: gitk:8701
 msgid "Reset type:"
 msgstr "Art des Zurücksetzens:"
 
-#: gitk:7755
+#: gitk:8705
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Harmlos: Arbeitskopie und Bereitstellung unverändert"
 
-#: gitk:7758
+#: gitk:8708
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr ""
 "Gemischt: Arbeitskopie unverändert,\n"
 "Bereitstellung zurückgesetzt"
 
-#: gitk:7761
+#: gitk:8711
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -868,21 +934,21 @@ msgstr ""
 "Hart: Arbeitskopie und Bereitstellung\n"
 "(Alle lokalen Änderungen werden gelöscht)"
 
-#: gitk:7777
+#: gitk:8728
 msgid "Resetting"
 msgstr "Zurücksetzen"
 
-#: gitk:7834
+#: gitk:8785
 msgid "Checking out"
 msgstr "Umstellen"
 
-#: gitk:7885
+#: gitk:8838
 msgid "Cannot delete the currently checked-out branch"
 msgstr ""
 "Der Zweig, auf den die Arbeitskopie momentan umgestellt ist, kann nicht "
 "gelöscht werden."
 
-#: gitk:7891
+#: gitk:8844
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -891,174 +957,174 @@ msgstr ""
 "Die Versionen auf Zweig »%s« existieren auf keinem anderen Zweig.\n"
 "Zweig »%s« trotzdem löschen?"
 
-#: gitk:7922
+#: gitk:8875
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Markierungen und Zweige: %s"
 
-#: gitk:7936
+#: gitk:8890
 msgid "Filter"
 msgstr "Filtern"
 
-#: gitk:8230
+#: gitk:9185
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
 msgstr ""
-"Fehler beim Lesen der Strukturinformationen; Zweige und Vorgänger/Nachfolger "
-"Informationen werden unvollständig sein."
+"Fehler beim Lesen der Strukturinformationen; Zweige und Informationen zu "
+"Vorgänger/Nachfolger werden unvollständig sein."
 
-#: gitk:9216
+#: gitk:10171
 msgid "Tag"
 msgstr "Markierung"
 
-#: gitk:9216
+#: gitk:10171
 msgid "Id"
 msgstr "Id"
 
-#: gitk:9262
+#: gitk:10219
 msgid "Gitk font chooser"
-msgstr "Gitk Schriften wählen"
+msgstr "Gitk-Schriften wählen"
 
-#: gitk:9279
+#: gitk:10236
 msgid "B"
 msgstr "F"
 
-#: gitk:9282
+#: gitk:10239
 msgid "I"
 msgstr "K"
 
-#: gitk:9375
+#: gitk:10334
 msgid "Gitk preferences"
-msgstr "Gitk Einstellungen"
+msgstr "Gitk-Einstellungen"
 
-#: gitk:9376
+#: gitk:10336
 msgid "Commit list display options"
-msgstr "Anzeige Versionsliste"
+msgstr "Anzeige der Versionsliste"
 
-#: gitk:9379
+#: gitk:10339
 msgid "Maximum graph width (lines)"
 msgstr "Maximale Graphenbreite (Zeilen)"
 
-#: gitk:9383
+#: gitk:10343
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximale Graphenbreite (% des Fensters)"
 
-#: gitk:9388
+#: gitk:10347
 msgid "Show local changes"
 msgstr "Lokale Änderungen anzeigen"
 
-#: gitk:9393
+#: gitk:10350
 msgid "Auto-select SHA1"
-msgstr "SHA1-Hashwert automatisch markieren"
+msgstr "SHA1-Hashwert automatisch auswählen"
 
-#: gitk:9398
+#: gitk:10354
 msgid "Diff display options"
-msgstr "Anzeige Vergleich"
+msgstr "Anzeige des Vergleichs"
 
-#: gitk:9400
+#: gitk:10356
 msgid "Tab spacing"
 msgstr "Tabulatorbreite"
 
-#: gitk:9404
+#: gitk:10359
 msgid "Display nearby tags"
-msgstr "Naheliegende Überschriften anzeigen"
+msgstr "Naheliegende Markierungen anzeigen"
 
-#: gitk:9409
+#: gitk:10362
 msgid "Limit diffs to listed paths"
 msgstr "Vergleich nur für angezeigte Pfade"
 
-#: gitk:9414
+#: gitk:10365
 msgid "Support per-file encodings"
 msgstr "Zeichenkodierung pro Datei ermitteln"
 
-#: gitk:9421
+#: gitk:10371 gitk:10436
 msgid "External diff tool"
-msgstr "Externes Vergleich-(Diff-)Programm"
+msgstr "Externes Diff-Programm"
 
-#: gitk:9423
+#: gitk:10373
 msgid "Choose..."
-msgstr "Wählen..."
+msgstr "Wählen ..."
 
-#: gitk:9428
+#: gitk:10378
 msgid "Colors: press to choose"
 msgstr "Farben: Klicken zum Wählen"
 
-#: gitk:9431
+#: gitk:10381
 msgid "Background"
 msgstr "Hintergrund"
 
-#: gitk:10153 gitk:10183
+#: gitk:10382 gitk:10412
 msgid "background"
 msgstr "Hintergrund"
 
-#: gitk:10156
+#: gitk:10385
 msgid "Foreground"
 msgstr "Vordergrund"
 
-#: gitk:10157
+#: gitk:10386
 msgid "foreground"
 msgstr "Vordergrund"
 
-#: gitk:10160
+#: gitk:10389
 msgid "Diff: old lines"
 msgstr "Vergleich: Alte Zeilen"
 
-#: gitk:10161
+#: gitk:10390
 msgid "diff old lines"
 msgstr "Vergleich - Alte Zeilen"
 
-#: gitk:10165
+#: gitk:10394
 msgid "Diff: new lines"
 msgstr "Vergleich: Neue Zeilen"
 
-#: gitk:10166
+#: gitk:10395
 msgid "diff new lines"
 msgstr "Vergleich - Neue Zeilen"
 
-#: gitk:10170
+#: gitk:10399
 msgid "Diff: hunk header"
 msgstr "Vergleich: Änderungstitel"
 
-#: gitk:10172
+#: gitk:10401
 msgid "diff hunk header"
 msgstr "Vergleich - Änderungstitel"
 
-#: gitk:10176
+#: gitk:10405
 msgid "Marked line bg"
-msgstr "Markierte Zeile Hintergrund"
+msgstr "Hintergrund für markierte Zeile"
 
-#: gitk:10178
+#: gitk:10407
 msgid "marked line background"
-msgstr "markierte Zeile Hintergrund"
+msgstr "Hintergrund für markierte Zeile"
 
-#: gitk:10182
+#: gitk:10411
 msgid "Select bg"
-msgstr "Hintergrundfarbe Auswählen"
+msgstr "Hintergrundfarbe auswählen"
 
-#: gitk:9459
+#: gitk:10415
 msgid "Fonts: press to choose"
 msgstr "Schriftart: Klicken zum Wählen"
 
-#: gitk:9461
+#: gitk:10417
 msgid "Main font"
 msgstr "Programmschriftart"
 
-#: gitk:9462
+#: gitk:10418
 msgid "Diff display font"
-msgstr "Vergleich"
+msgstr "Schriftart für Vergleich"
 
-#: gitk:9463
+#: gitk:10419
 msgid "User interface font"
 msgstr "Beschriftungen"
 
-#: gitk:9488
+#: gitk:10446
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: Farbe wählen für %s"
 
-#: gitk:9934
+#: gitk:10893
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -1066,24 +1132,24 @@ msgstr ""
 "Gitk läuft nicht mit dieser Version von Tcl/Tk.\n"
 "Gitk benötigt mindestens Tcl/Tk 8.4."
 
-#: gitk:10047
+#: gitk:11020
 msgid "Cannot find a git repository here."
 msgstr "Kein Git-Projektarchiv gefunden."
 
-#: gitk:10051
+#: gitk:11024
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Git-Verzeichnis »%s« wurde nicht gefunden."
 
-#: gitk:10098
+#: gitk:11071
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Mehrdeutige Angabe »%s«: Sowohl Version als auch Dateiname existiert."
 
-#: gitk:10110
+#: gitk:11083
 msgid "Bad arguments to gitk:"
 msgstr "Falsche Kommandozeilen-Parameter für gitk:"
 
-#: gitk:10170
+#: gitk:11167
 msgid "Command line"
 msgstr "Kommandozeile"
index 3f99361ed03b8d5202cb17e894c65678364fc1ed..1e7e2d8387efb810afb3b9c9d279cc59f52d932e 100755 (executable)
@@ -458,8 +458,8 @@ sub filter_snapshot_fmts {
        @fmts = map {
                exists $known_snapshot_format_aliases{$_} ?
                       $known_snapshot_format_aliases{$_} : $_} @fmts;
-       @fmts = grep(exists $known_snapshot_formats{$_}, @fmts);
-
+       @fmts = grep {
+               exists $known_snapshot_formats{$_} } @fmts;
 }
 
 our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
@@ -690,9 +690,10 @@ sub evaluate_path_info {
                # format key itself, with a prepended dot
                while (my ($fmt, $opt) = each %known_snapshot_formats) {
                        my $hash = $refname;
-                       my $sfx;
-                       $hash =~ s/(\Q$opt->{'suffix'}\E|\Q.$fmt\E)$//;
-                       next unless $sfx = $1;
+                       unless ($hash =~ s/(\Q$opt->{'suffix'}\E|\Q.$fmt\E)$//) {
+                               next;
+                       }
+                       my $sfx = $1;
                        # a valid suffix was found, so set the snapshot format
                        # and reset the hash parameter
                        $input_params{'snapshot_format'} = $fmt;
@@ -828,7 +829,7 @@ if (!defined $action) {
 if (!defined($actions{$action})) {
        die_error(400, "Unknown action");
 }
-if ($action !~ m/^(opml|project_list|project_index)$/ &&
+if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
     !$project) {
        die_error(400, "Project needed");
 }
@@ -838,7 +839,7 @@ exit;
 ## ======================================================================
 ## action links
 
-sub href (%) {
+sub href {
        my %params = @_;
        # default is to use -absolute url() i.e. $my_uri
        my $href = $params{-full} ? $my_url : $my_uri;
@@ -1036,7 +1037,7 @@ sub esc_url {
 }
 
 # replace invalid utf8 character with SUBSTITUTION sequence
-sub esc_html ($;%) {
+sub esc_html {
        my $str = shift;
        my %opts = @_;
 
@@ -1235,7 +1236,7 @@ sub chop_and_escape_str {
        if ($chopped eq $str) {
                return esc_html($chopped);
        } else {
-               $str =~ s/([[:cntrl:]])/?/g;
+               $str =~ s/[[:cntrl:]]/?/g;
                return $cgi->span({-title=>$str}, esc_html($chopped));
        }
 }
@@ -1296,7 +1297,7 @@ use constant {
 };
 
 # submodule/subproject, a commit object reference
-sub S_ISGITLINK($) {
+sub S_ISGITLINK {
        my $mode = shift;
 
        return (($mode & S_IFMT) == S_IFGITLINK)
@@ -1458,6 +1459,7 @@ sub format_subject_html {
        $extra = '' unless defined($extra);
 
        if (length($short) < length($long)) {
+               $long =~ s/[[:cntrl:]]/?/g;
                return $cgi->a({-href => $href, -class => "list subject",
                                -title => to_utf8($long)},
                       esc_html($short) . $extra);
@@ -1838,7 +1840,7 @@ sub git_cmd {
 # Try to avoid using this function wherever possible.
 sub quote_command {
        return join(' ',
-                   map( { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ ));
+               map { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ );
 }
 
 # get HEAD ref of given project as hash
@@ -2050,7 +2052,7 @@ sub git_get_project_description {
        my $path = shift;
 
        $git_dir = "$projectroot/$path";
-       open my $fd, "$git_dir/description"
+       open my $fd, '<', "$git_dir/description"
                or return git_get_project_config('description');
        my $descr = <$fd>;
        close $fd;
@@ -2065,18 +2067,17 @@ sub git_get_project_ctags {
        my $ctags = {};
 
        $git_dir = "$projectroot/$path";
-       unless (opendir D, "$git_dir/ctags") {
-               return $ctags;
-       }
-       foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir(D)) {
-               open CT, $_ or next;
-               my $val = <CT>;
+       opendir my $dh, "$git_dir/ctags"
+               or return $ctags;
+       foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir($dh)) {
+               open my $ct, '<', $_ or next;
+               my $val = <$ct>;
                chomp $val;
-               close CT;
+               close $ct;
                my $ctag = $_; $ctag =~ s#.*/##;
                $ctags->{$ctag} = $val;
        }
-       closedir D;
+       closedir $dh;
        $ctags;
 }
 
@@ -2129,7 +2130,7 @@ sub git_get_project_url_list {
        my $path = shift;
 
        $git_dir = "$projectroot/$path";
-       open my $fd, "$git_dir/cloneurl"
+       open my $fd, '<', "$git_dir/cloneurl"
                or return wantarray ?
                @{ config_to_multi(git_get_project_config('url')) } :
                   config_to_multi(git_get_project_config('url'));
@@ -2187,7 +2188,7 @@ sub git_get_projects_list {
                # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
                # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
                my %paths;
-               open my ($fd), $projects_list or return;
+               open my $fd, '<', $projects_list or return;
        PROJECT:
                while (my $line = <$fd>) {
                        chomp $line;
@@ -2250,7 +2251,7 @@ sub git_get_project_list_from_file {
        # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
        # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
        if (-f $projects_list) {
-               open (my $fd , $projects_list);
+               open(my $fd, '<', $projects_list);
                while (my $line = <$fd>) {
                        chomp $line;
                        my ($pr, $ow) = split ' ', $line;
@@ -2615,7 +2616,7 @@ sub parsed_difftree_line {
 }
 
 # parse line of git-ls-tree output
-sub parse_ls_tree_line ($;%) {
+sub parse_ls_tree_line {
        my $line = shift;
        my %opts = @_;
        my %res;
@@ -2804,18 +2805,18 @@ sub mimetype_guess_file {
        -r $mimemap or return undef;
 
        my %mimemap;
-       open(MIME, $mimemap) or return undef;
-       while (<MIME>) {
+       open(my $mh, '<', $mimemap) or return undef;
+       while (<$mh>) {
                next if m/^#/; # skip comments
-               my ($mime, $exts) = split(/\t+/);
+               my ($mimetype, $exts) = split(/\t+/);
                if (defined $exts) {
                        my @exts = split(/\s+/, $exts);
                        foreach my $ext (@exts) {
-                               $mimemap{$ext} = $mime;
+                               $mimemap{$ext} = $mimetype;
                        }
                }
        }
-       close(MIME);
+       close($mh);
 
        $filename =~ /\.([^.]*)$/;
        return $mimemap{$1};
@@ -3213,7 +3214,6 @@ sub git_print_header_div {
              "\n</div>\n";
 }
 
-#sub git_print_authorship (\%) {
 sub git_print_authorship {
        my $co = shift;
 
@@ -3269,8 +3269,7 @@ sub git_print_page_path {
        print "<br/></div>\n";
 }
 
-# sub git_print_log (\@;%) {
-sub git_print_log ($;%) {
+sub git_print_log {
        my $log = shift;
        my %opts = @_;
 
@@ -3328,7 +3327,7 @@ sub git_get_link_target {
        open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
                or return;
        {
-               local $/;
+               local $/ = undef;
                $link_target = <$fd>;
        }
        close $fd
@@ -3341,10 +3340,7 @@ sub git_get_link_target {
 # return target of link relative to top directory (top tree);
 # return undef if it is not possible (including absolute links).
 sub normalize_link_target {
-       my ($link_target, $basedir, $hash_base) = @_;
-
-       # we can normalize symlink target only if $hash_base is provided
-       return unless $hash_base;
+       my ($link_target, $basedir) = @_;
 
        # absolute symlinks (beginning with '/') cannot be normalized
        return if (substr($link_target, 0, 1) eq '/');
@@ -3400,7 +3396,7 @@ sub git_print_tree_entry {
                if (S_ISLNK(oct $t->{'mode'})) {
                        my $link_target = git_get_link_target($t->{'hash'});
                        if ($link_target) {
-                               my $norm_target = normalize_link_target($link_target, $basedir, $hash_base);
+                               my $norm_target = normalize_link_target($link_target, $basedir);
                                if (defined $norm_target) {
                                        print " -> " .
                                              $cgi->a({-href => href(action=>"object", hash_base=>$hash_base,
@@ -3993,7 +3989,7 @@ sub fill_project_list_info {
                            ($pname !~ /\/$/) &&
                            (-d "$projectroot/$pname")) {
                                $pr->{'forks'} = "-d $projectroot/$pname";
-                       }       else {
+                       } else {
                                $pr->{'forks'} = 0;
                        }
                }
@@ -4803,11 +4799,10 @@ sub git_blob_plain {
                -content_disposition =>
                        ($sandbox ? 'attachment' : 'inline')
                        . '; filename="' . $save_as . '"');
-       undef $/;
+       local $/ = undef;
        binmode STDOUT, ':raw';
        print <$fd>;
        binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
-       $/ = "\n";
        close $fd;
 }
 
@@ -4909,12 +4904,16 @@ sub git_tree {
                }
        }
        die_error(404, "No such tree") unless defined($hash);
-       $/ = "\0";
-       open my $fd, "-|", git_cmd(), "ls-tree", '-z', $hash
-               or die_error(500, "Open git-ls-tree failed");
-       my @entries = map { chomp; $_ } <$fd>;
-       close $fd or die_error(404, "Reading tree failed");
-       $/ = "\n";
+
+       my @entries = ();
+       {
+               local $/ = "\0";
+               open my $fd, "-|", git_cmd(), "ls-tree", '-z', $hash
+                       or die_error(500, "Open git-ls-tree failed");
+               @entries = map { chomp; $_ } <$fd>;
+               close $fd
+                       or die_error(404, "Reading tree failed");
+       }
 
        my $refs = git_get_references();
        my $ref = format_ref_marker($refs, $hash_base);
@@ -5809,7 +5808,7 @@ sub git_search {
 
                print "<table class=\"pickaxe search\">\n";
                my $alternate = 1;
-               $/ = "\n";
+               local $/ = "\n";
                open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
                        '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
                        ($search_use_regexp ? '--pickaxe-regex' : ());
@@ -5879,7 +5878,7 @@ sub git_search {
                print "<table class=\"grep_search\">\n";
                my $alternate = 1;
                my $matches = 0;
-               $/ = "\n";
+               local $/ = "\n";
                open my $fd, "-|", git_cmd(), 'grep', '-n',
                        $search_use_regexp ? ('-E', '-i') : '-F',
                        $searchtext, $co{'tree'};
@@ -6282,7 +6281,7 @@ XML
        # end of feed
        if ($format eq 'rss') {
                print "</channel>\n</rss>\n";
-       }       elsif ($format eq 'atom') {
+       } elsif ($format eq 'atom') {
                print "</feed>\n";
        }
 }
diff --git a/graph.c b/graph.c
index 06fbeb6c2416d0c1c5fd7df1a12924656592cdc3..f8d7a5c0f6d17538a72b75e8de9dd092829a0262 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -47,20 +47,6 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);
  * - Limit the number of columns, similar to the way gitk does.
  *   If we reach more than a specified number of columns, omit
  *   sections of some columns.
- *
- * - The output during the GRAPH_PRE_COMMIT and GRAPH_COLLAPSING states
- *   could be made more compact by printing horizontal lines, instead of
- *   long diagonal lines.  For example, during collapsing, something like
- *   this:          instead of this:
- *   | | | | |      | | | | |
- *   | |_|_|/       | | | |/
- *   |/| | |        | | |/|
- *   | | | |        | |/| |
- *                  |/| | |
- *                  | | | |
- *
- *   If there are several parallel diagonal lines, they will need to be
- *   replaced with horizontal lines on subsequent rows.
  */
 
 struct column {
@@ -982,6 +968,9 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
 {
        int i;
        int *tmp_mapping;
+       short used_horizontal = 0;
+       int horizontal_edge = -1;
+       int horizontal_edge_target = -1;
 
        /*
         * Clear out the new_mapping array
@@ -1019,6 +1008,23 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                         * Move to the left by one
                         */
                        graph->new_mapping[i - 1] = target;
+                       /*
+                        * If there isn't already an edge moving horizontally
+                        * select this one.
+                        */
+                       if (horizontal_edge == -1) {
+                               int j;
+                               horizontal_edge = i;
+                               horizontal_edge_target = target;
+                               /*
+                                * The variable target is the index of the graph
+                                * column, and therefore target*2+3 is the
+                                * actual screen column of the first horizontal
+                                * line.
+                                */
+                               for (j = (target * 2)+3; j < (i - 2); j += 2)
+                                       graph->new_mapping[j] = target;
+                       }
                } else if (graph->new_mapping[i - 1] == target) {
                        /*
                         * There is a branch line to our left
@@ -1039,10 +1045,21 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                         *
                         * The space just to the left of this
                         * branch should always be empty.
+                        *
+                        * The branch to the left of that space
+                        * should be our eventual target.
                         */
                        assert(graph->new_mapping[i - 1] > target);
                        assert(graph->new_mapping[i - 2] < 0);
+                       assert(graph->new_mapping[i - 3] == target);
                        graph->new_mapping[i - 2] = target;
+                       /*
+                        * Mark this branch as the horizontal edge to
+                        * prevent any other edges from moving
+                        * horizontally.
+                        */
+                       if (horizontal_edge == -1)
+                               horizontal_edge = i;
                }
        }
 
@@ -1061,8 +1078,23 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                        strbuf_addch(sb, ' ');
                else if (target * 2 == i)
                        strbuf_write_column(sb, &graph->new_columns[target], '|');
-               else
+               else if (target == horizontal_edge_target &&
+                        i != horizontal_edge - 1) {
+                               /*
+                                * Set the mappings for all but the
+                                * first segment to -1 so that they
+                                * won't continue into the next line.
+                                */
+                               if (i != (target * 2)+3)
+                                       graph->new_mapping[i] = -1;
+                               used_horizontal = 1;
+                       strbuf_write_column(sb, &graph->new_columns[target], '_');
+               } else {
+                       if (used_horizontal && i < horizontal_edge)
+                               graph->new_mapping[i] = -1;
                        strbuf_write_column(sb, &graph->new_columns[target], '/');
+
+               }
        }
 
        graph_pad_horizontally(graph, sb, graph->mapping_size);
diff --git a/grep.h b/grep.h
index a67005de62d1442e7ba6a8dc27320225a0d55819..464e272edf4594539915a89d68628fd3959b8ee3 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -61,23 +61,23 @@ struct grep_opt {
        struct grep_expr *pattern_expression;
        int prefix_length;
        regex_t regexp;
-       unsigned linenum:1;
-       unsigned invert:1;
-       unsigned status_only:1;
-       unsigned name_only:1;
-       unsigned unmatch_name_only:1;
-       unsigned count:1;
-       unsigned word_regexp:1;
-       unsigned fixed:1;
-       unsigned all_match:1;
+       int linenum;
+       int invert;
+       int status_only;
+       int name_only;
+       int unmatch_name_only;
+       int count;
+       int word_regexp;
+       int fixed;
+       int all_match;
 #define GREP_BINARY_DEFAULT    0
 #define GREP_BINARY_NOMATCH    1
 #define GREP_BINARY_TEXT       2
-       unsigned binary:2;
-       unsigned extended:1;
-       unsigned relative:1;
-       unsigned pathname:1;
-       unsigned null_following_name:1;
+       int binary;
+       int extended;
+       int relative;
+       int pathname;
+       int null_following_name;
        int color;
        char color_match[COLOR_MAXLEN];
        const char *color_external;
index ebb3bedb074202a29e2356845012bd1ffae0dc19..47cf43c3cdd2bcd7638360a130fc8ef04ce132e9 100644 (file)
@@ -84,7 +84,8 @@ int main(int argc, const char **argv)
 
        git_extract_argv0_path(argv[0]);
 
-       argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0);
+       argc = parse_options(argc, argv, NULL, hash_object_options,
+                            hash_object_usage, 0);
 
        if (write_object) {
                prefix = setup_git_directory();
index e16a0ad3f97ef49930a599d3a800a0c7fad317e0..e4ea395e029446920f570d888d93ba9ae249f18c 100644 (file)
@@ -1,6 +1,5 @@
 #include "cache.h"
 #include "commit.h"
-#include "pack.h"
 #include "tag.h"
 #include "blob.h"
 #include "http.h"
@@ -27,7 +26,6 @@ enum XML_Status {
 #endif
 
 #define PREV_BUF_SIZE 4096
-#define RANGE_HEADER_SIZE 30
 
 /* DAV methods */
 #define DAV_LOCK "LOCK"
@@ -76,8 +74,6 @@ static int pushing;
 static int aborted;
 static signed char remote_dir_exists[256];
 
-static struct curl_slist *no_pragma_header;
-
 static int push_verbosely;
 static int push_all = MATCH_REFS_NONE;
 static int force_all;
@@ -119,19 +115,10 @@ struct transfer_request
        struct remote_lock *lock;
        struct curl_slist *headers;
        struct buffer buffer;
-       char filename[PATH_MAX];
-       char tmpfile[PATH_MAX];
-       int local_fileno;
-       FILE *local_stream;
        enum transfer_state state;
        CURLcode curl_result;
        char errorstr[CURL_ERROR_SIZE];
        long http_code;
-       unsigned char real_sha1[20];
-       git_SHA_CTX c;
-       z_stream stream;
-       int zret;
-       int rename;
        void *userData;
        struct active_request_slot *slot;
        struct transfer_request *next;
@@ -237,15 +224,6 @@ static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum d
        return dav_headers;
 }
 
-static void append_remote_object_url(struct strbuf *buf, const char *url,
-                                    const char *hex,
-                                    int only_two_digit_prefix)
-{
-       strbuf_addf(buf, "%sobjects/%.*s/", url, 2, hex);
-       if (!only_two_digit_prefix)
-               strbuf_addf(buf, "%s", hex+2);
-}
-
 static void finish_request(struct transfer_request *request);
 static void release_request(struct transfer_request *request);
 
@@ -259,163 +237,29 @@ static void process_response(void *callback_data)
 
 #ifdef USE_CURL_MULTI
 
-static char *get_remote_object_url(const char *url, const char *hex,
-                                  int only_two_digit_prefix)
-{
-       struct strbuf buf = STRBUF_INIT;
-       append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
-       return strbuf_detach(&buf, NULL);
-}
-
-static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
-                              void *data)
-{
-       unsigned char expn[4096];
-       size_t size = eltsize * nmemb;
-       int posn = 0;
-       struct transfer_request *request = (struct transfer_request *)data;
-       do {
-               ssize_t retval = xwrite(request->local_fileno,
-                                      (char *) ptr + posn, size - posn);
-               if (retval < 0)
-                       return posn;
-               posn += retval;
-       } while (posn < size);
-
-       request->stream.avail_in = size;
-       request->stream.next_in = ptr;
-       do {
-               request->stream.next_out = expn;
-               request->stream.avail_out = sizeof(expn);
-               request->zret = git_inflate(&request->stream, Z_SYNC_FLUSH);
-               git_SHA1_Update(&request->c, expn,
-                           sizeof(expn) - request->stream.avail_out);
-       } while (request->stream.avail_in && request->zret == Z_OK);
-       data_received++;
-       return size;
-}
-
 static void start_fetch_loose(struct transfer_request *request)
 {
-       char *hex = sha1_to_hex(request->obj->sha1);
-       char *filename;
-       char prevfile[PATH_MAX];
-       char *url;
-       int prevlocal;
-       unsigned char prev_buf[PREV_BUF_SIZE];
-       ssize_t prev_read = 0;
-       long prev_posn = 0;
-       char range[RANGE_HEADER_SIZE];
-       struct curl_slist *range_header = NULL;
        struct active_request_slot *slot;
+       struct http_object_request *obj_req;
 
-       filename = sha1_file_name(request->obj->sha1);
-       snprintf(request->filename, sizeof(request->filename), "%s", filename);
-       snprintf(request->tmpfile, sizeof(request->tmpfile),
-                "%s.temp", filename);
-
-       snprintf(prevfile, sizeof(prevfile), "%s.prev", request->filename);
-       unlink_or_warn(prevfile);
-       rename(request->tmpfile, prevfile);
-       unlink_or_warn(request->tmpfile);
-
-       if (request->local_fileno != -1)
-               error("fd leakage in start: %d", request->local_fileno);
-       request->local_fileno = open(request->tmpfile,
-                                    O_WRONLY | O_CREAT | O_EXCL, 0666);
-       /* This could have failed due to the "lazy directory creation";
-        * try to mkdir the last path component.
-        */
-       if (request->local_fileno < 0 && errno == ENOENT) {
-               char *dir = strrchr(request->tmpfile, '/');
-               if (dir) {
-                       *dir = 0;
-                       mkdir(request->tmpfile, 0777);
-                       *dir = '/';
-               }
-               request->local_fileno = open(request->tmpfile,
-                                            O_WRONLY | O_CREAT | O_EXCL, 0666);
-       }
-
-       if (request->local_fileno < 0) {
+       obj_req = new_http_object_request(repo->url, request->obj->sha1);
+       if (obj_req == NULL) {
                request->state = ABORTED;
-               error("Couldn't create temporary file %s for %s: %s",
-                     request->tmpfile, request->filename, strerror(errno));
                return;
        }
 
-       memset(&request->stream, 0, sizeof(request->stream));
-
-       git_inflate_init(&request->stream);
-
-       git_SHA1_Init(&request->c);
-
-       url = get_remote_object_url(repo->url, hex, 0);
-       request->url = xstrdup(url);
-
-       /* If a previous temp file is present, process what was already
-          fetched. */
-       prevlocal = open(prevfile, O_RDONLY);
-       if (prevlocal != -1) {
-               do {
-                       prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
-                       if (prev_read>0) {
-                               if (fwrite_sha1_file(prev_buf,
-                                                    1,
-                                                    prev_read,
-                                                    request) == prev_read) {
-                                       prev_posn += prev_read;
-                               } else {
-                                       prev_read = -1;
-                               }
-                       }
-               } while (prev_read > 0);
-               close(prevlocal);
-       }
-       unlink_or_warn(prevfile);
-
-       /* Reset inflate/SHA1 if there was an error reading the previous temp
-          file; also rewind to the beginning of the local file. */
-       if (prev_read == -1) {
-               memset(&request->stream, 0, sizeof(request->stream));
-               git_inflate_init(&request->stream);
-               git_SHA1_Init(&request->c);
-               if (prev_posn>0) {
-                       prev_posn = 0;
-                       lseek(request->local_fileno, 0, SEEK_SET);
-                       ftruncate(request->local_fileno, 0);
-               }
-       }
-
-       slot = get_active_slot();
+       slot = obj_req->slot;
        slot->callback_func = process_response;
        slot->callback_data = request;
        request->slot = slot;
-
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, request);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
-       curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
-
-       /* If we have successfully processed data from a previous fetch
-          attempt, only fetch the data we don't already have. */
-       if (prev_posn>0) {
-               if (push_verbosely)
-                       fprintf(stderr,
-                               "Resuming fetch of object %s at byte %ld\n",
-                               hex, prev_posn);
-               sprintf(range, "Range: bytes=%ld-", prev_posn);
-               range_header = curl_slist_append(range_header, range);
-               curl_easy_setopt(slot->curl,
-                                CURLOPT_HTTPHEADER, range_header);
-       }
+       request->userData = obj_req;
 
        /* Try to get the request started, abort the request on error */
        request->state = RUN_FETCH_LOOSE;
        if (!start_active_slot(slot)) {
                fprintf(stderr, "Unable to start GET request\n");
                repo->can_update_info_refs = 0;
+               release_http_object_request(obj_req);
                release_request(request);
        }
 }
@@ -449,16 +293,10 @@ static void start_mkcol(struct transfer_request *request)
 
 static void start_fetch_packed(struct transfer_request *request)
 {
-       char *url;
        struct packed_git *target;
-       FILE *packfile;
-       char *filename;
-       long prev_posn = 0;
-       char range[RANGE_HEADER_SIZE];
-       struct curl_slist *range_header = NULL;
 
        struct transfer_request *check_request = request_queue_head;
-       struct active_request_slot *slot;
+       struct http_pack_request *preq;
 
        target = find_sha1_pack(request->obj->sha1, repo->packs);
        if (!target) {
@@ -471,66 +309,35 @@ static void start_fetch_packed(struct transfer_request *request)
        fprintf(stderr, "Fetching pack %s\n", sha1_to_hex(target->sha1));
        fprintf(stderr, " which contains %s\n", sha1_to_hex(request->obj->sha1));
 
-       filename = sha1_pack_name(target->sha1);
-       snprintf(request->filename, sizeof(request->filename), "%s", filename);
-       snprintf(request->tmpfile, sizeof(request->tmpfile),
-                "%s.temp", filename);
-
-       url = xmalloc(strlen(repo->url) + 64);
-       sprintf(url, "%sobjects/pack/pack-%s.pack",
-               repo->url, sha1_to_hex(target->sha1));
+       preq = new_http_pack_request(target, repo->url);
+       if (preq == NULL) {
+               release_http_pack_request(preq);
+               repo->can_update_info_refs = 0;
+               return;
+       }
+       preq->lst = &repo->packs;
 
        /* Make sure there isn't another open request for this pack */
        while (check_request) {
                if (check_request->state == RUN_FETCH_PACKED &&
-                   !strcmp(check_request->url, url)) {
-                       free(url);
+                   !strcmp(check_request->url, preq->url)) {
+                       release_http_pack_request(preq);
                        release_request(request);
                        return;
                }
                check_request = check_request->next;
        }
 
-       packfile = fopen(request->tmpfile, "a");
-       if (!packfile) {
-               fprintf(stderr, "Unable to open local file %s for pack",
-                       request->tmpfile);
-               repo->can_update_info_refs = 0;
-               free(url);
-               return;
-       }
-
-       slot = get_active_slot();
-       slot->callback_func = process_response;
-       slot->callback_data = request;
-       request->slot = slot;
-       request->local_stream = packfile;
-       request->userData = target;
-
-       request->url = url;
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
-       slot->local = packfile;
-
-       /* If there is data present from a previous transfer attempt,
-          resume where it left off */
-       prev_posn = ftell(packfile);
-       if (prev_posn>0) {
-               if (push_verbosely)
-                       fprintf(stderr,
-                               "Resuming fetch of pack %s at byte %ld\n",
-                               sha1_to_hex(target->sha1), prev_posn);
-               sprintf(range, "Range: bytes=%ld-", prev_posn);
-               range_header = curl_slist_append(range_header, range);
-               curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
-       }
+       preq->slot->callback_func = process_response;
+       preq->slot->callback_data = request;
+       request->slot = preq->slot;
+       request->userData = preq;
 
        /* Try to get the request started, abort the request on error */
        request->state = RUN_FETCH_PACKED;
-       if (!start_active_slot(slot)) {
+       if (!start_active_slot(preq->slot)) {
                fprintf(stderr, "Unable to start GET request\n");
+               release_http_pack_request(preq);
                repo->can_update_info_refs = 0;
                release_request(request);
        }
@@ -711,19 +518,14 @@ static void release_request(struct transfer_request *request)
                        entry->next = entry->next->next;
        }
 
-       if (request->local_fileno != -1)
-               close(request->local_fileno);
-       if (request->local_stream)
-               fclose(request->local_stream);
        free(request->url);
        free(request);
 }
 
 static void finish_request(struct transfer_request *request)
 {
-       struct stat st;
-       struct packed_git *target;
-       struct packed_git **lst;
+       struct http_pack_request *preq;
+       struct http_object_request *obj_req;
 
        request->curl_result = request->slot->curl_result;
        request->http_code = request->slot->http_code;
@@ -778,76 +580,46 @@ static void finish_request(struct transfer_request *request)
                        aborted = 1;
                }
        } else if (request->state == RUN_FETCH_LOOSE) {
-               close(request->local_fileno); request->local_fileno = -1;
+               obj_req = (struct http_object_request *)request->userData;
 
-               if (request->curl_result != CURLE_OK &&
-                   request->http_code != 416) {
-                       if (stat(request->tmpfile, &st) == 0) {
-                               if (st.st_size == 0)
-                                       unlink_or_warn(request->tmpfile);
-                       }
-               } else {
-                       if (request->http_code == 416)
-                               warning("requested range invalid; we may already have all the data.");
-
-                       git_inflate_end(&request->stream);
-                       git_SHA1_Final(request->real_sha1, &request->c);
-                       if (request->zret != Z_STREAM_END) {
-                               unlink_or_warn(request->tmpfile);
-                       } else if (hashcmp(request->obj->sha1, request->real_sha1)) {
-                               unlink_or_warn(request->tmpfile);
-                       } else {
-                               request->rename =
-                                       move_temp_to_file(
-                                               request->tmpfile,
-                                               request->filename);
-                               if (request->rename == 0) {
-                                       request->obj->flags |= (LOCAL | REMOTE);
-                               }
-                       }
-               }
+               if (finish_http_object_request(obj_req) == 0)
+                       if (obj_req->rename == 0)
+                               request->obj->flags |= (LOCAL | REMOTE);
 
                /* Try fetching packed if necessary */
-               if (request->obj->flags & LOCAL)
+               if (request->obj->flags & LOCAL) {
+                       release_http_object_request(obj_req);
                        release_request(request);
-               else
+               else
                        start_fetch_packed(request);
 
        } else if (request->state == RUN_FETCH_PACKED) {
+               int fail = 1;
                if (request->curl_result != CURLE_OK) {
                        fprintf(stderr, "Unable to get pack file %s\n%s",
                                request->url, curl_errorstr);
-                       repo->can_update_info_refs = 0;
                } else {
-                       off_t pack_size = ftell(request->local_stream);
-
-                       fclose(request->local_stream);
-                       request->local_stream = NULL;
-                       if (!move_temp_to_file(request->tmpfile,
-                                              request->filename)) {
-                               target = (struct packed_git *)request->userData;
-                               target->pack_size = pack_size;
-                               lst = &repo->packs;
-                               while (*lst != target)
-                                       lst = &((*lst)->next);
-                               *lst = (*lst)->next;
-
-                               if (!verify_pack(target))
-                                       install_packed_git(target);
-                               else
-                                       repo->can_update_info_refs = 0;
+                       preq = (struct http_pack_request *)request->userData;
+
+                       if (preq) {
+                               if (finish_http_pack_request(preq) > 0)
+                                       fail = 0;
+                               release_http_pack_request(preq);
                        }
                }
+               if (fail)
+                       repo->can_update_info_refs = 0;
                release_request(request);
        }
 }
 
 #ifdef USE_CURL_MULTI
+static int is_running_queue;
 static int fill_active_slot(void *unused)
 {
        struct transfer_request *request;
 
-       if (aborted)
+       if (aborted || !is_running_queue)
                return 0;
 
        for (request = request_queue_head; request; request = request->next) {
@@ -890,8 +662,6 @@ static void add_fetch_request(struct object *obj)
        request->url = NULL;
        request->lock = NULL;
        request->headers = NULL;
-       request->local_fileno = -1;
-       request->local_stream = NULL;
        request->state = NEED_FETCH;
        request->next = request_queue_head;
        request_queue_head = request;
@@ -930,8 +700,6 @@ static int add_send_request(struct object *obj, struct remote_lock *lock)
        request->url = NULL;
        request->lock = lock;
        request->headers = NULL;
-       request->local_fileno = -1;
-       request->local_stream = NULL;
        request->state = NEED_PUSH;
        request->next = request_queue_head;
        request_queue_head = request;
@@ -944,176 +712,23 @@ static int add_send_request(struct object *obj, struct remote_lock *lock)
        return 1;
 }
 
-static int fetch_index(unsigned char *sha1)
-{
-       char *hex = sha1_to_hex(sha1);
-       char *filename;
-       char *url;
-       char tmpfile[PATH_MAX];
-       long prev_posn = 0;
-       char range[RANGE_HEADER_SIZE];
-       struct curl_slist *range_header = NULL;
-
-       FILE *indexfile;
-       struct active_request_slot *slot;
-       struct slot_results results;
-
-       /* Don't use the index if the pack isn't there */
-       url = xmalloc(strlen(repo->url) + 64);
-       sprintf(url, "%sobjects/pack/pack-%s.pack", repo->url, hex);
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (results.curl_result != CURLE_OK) {
-                       free(url);
-                       return error("Unable to verify pack %s is available",
-                                    hex);
-               }
-       } else {
-               free(url);
-               return error("Unable to start request");
-       }
-
-       if (has_pack_index(sha1)) {
-               free(url);
-               return 0;
-       }
-
-       if (push_verbosely)
-               fprintf(stderr, "Getting index for pack %s\n", hex);
-
-       sprintf(url, "%sobjects/pack/pack-%s.idx", repo->url, hex);
-
-       filename = sha1_pack_index_name(sha1);
-       snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
-       indexfile = fopen(tmpfile, "a");
-       if (!indexfile) {
-               free(url);
-               return error("Unable to open local file %s for pack index",
-                            tmpfile);
-       }
-
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
-       slot->local = indexfile;
-
-       /* If there is data present from a previous transfer attempt,
-          resume where it left off */
-       prev_posn = ftell(indexfile);
-       if (prev_posn>0) {
-               if (push_verbosely)
-                       fprintf(stderr,
-                               "Resuming fetch of index for pack %s at byte %ld\n",
-                               hex, prev_posn);
-               sprintf(range, "Range: bytes=%ld-", prev_posn);
-               range_header = curl_slist_append(range_header, range);
-               curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
-       }
-
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (results.curl_result != CURLE_OK) {
-                       free(url);
-                       fclose(indexfile);
-                       return error("Unable to get pack index %s\n%s", url,
-                                    curl_errorstr);
-               }
-       } else {
-               free(url);
-               fclose(indexfile);
-               return error("Unable to start request");
-       }
-
-       free(url);
-       fclose(indexfile);
-
-       return move_temp_to_file(tmpfile, filename);
-}
-
-static int setup_index(unsigned char *sha1)
-{
-       struct packed_git *new_pack;
-
-       if (fetch_index(sha1))
-               return -1;
-
-       new_pack = parse_pack_index(sha1);
-       new_pack->next = repo->packs;
-       repo->packs = new_pack;
-       return 0;
-}
-
 static int fetch_indices(void)
 {
-       unsigned char sha1[20];
-       char *url;
-       struct strbuf buffer = STRBUF_INIT;
-       char *data;
-       int i = 0;
-
-       struct active_request_slot *slot;
-       struct slot_results results;
+       int ret;
 
        if (push_verbosely)
                fprintf(stderr, "Getting pack list\n");
 
-       url = xmalloc(strlen(repo->url) + 20);
-       sprintf(url, "%sobjects/info/packs", repo->url);
-
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (results.curl_result != CURLE_OK) {
-                       strbuf_release(&buffer);
-                       free(url);
-                       if (results.http_code == 404)
-                               return 0;
-                       else
-                               return error("%s", curl_errorstr);
-               }
-       } else {
-               strbuf_release(&buffer);
-               free(url);
-               return error("Unable to start request");
-       }
-       free(url);
-
-       data = buffer.buf;
-       while (i < buffer.len) {
-               switch (data[i]) {
-               case 'P':
-                       i++;
-                       if (i + 52 < buffer.len &&
-                           !prefixcmp(data + i, " pack-") &&
-                           !prefixcmp(data + i + 46, ".pack\n")) {
-                               get_sha1_hex(data + i + 6, sha1);
-                               setup_index(sha1);
-                               i += 51;
-                               break;
-                       }
-               default:
-                       while (data[i] != '\n')
-                               i++;
-               }
-               i++;
+       switch (http_get_info_packs(repo->url, &repo->packs)) {
+       case HTTP_OK:
+       case HTTP_MISSING_TARGET:
+               ret = 0;
+               break;
+       default:
+               ret = -1;
        }
 
-       strbuf_release(&buffer);
-       return 0;
+       return ret;
 }
 
 static void one_remote_object(const char *hex)
@@ -1844,7 +1459,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
        return 1;
 }
 
-static struct ref *remote_refs, **remote_tail;
+static struct ref *remote_refs;
 
 static void one_remote_ref(char *refname)
 {
@@ -1874,27 +1489,15 @@ static void one_remote_ref(char *refname)
                }
        }
 
-       *remote_tail = ref;
-       remote_tail = &ref->next;
+       ref->next = remote_refs;
+       remote_refs = ref;
 }
 
 static void get_dav_remote_heads(void)
 {
-       remote_tail = &remote_refs;
        remote_ls("refs/", (PROCESS_FILES | PROCESS_DIRS | RECURSIVE), process_ls_ref, NULL);
 }
 
-static int is_zero_sha1(const unsigned char *sha1)
-{
-       int i;
-
-       for (i = 0; i < 20; i++) {
-               if (*sha1++)
-                       return 0;
-       }
-       return 1;
-}
-
 static void add_remote_info_ref(struct remote_ls_ctx *ls)
 {
        struct strbuf *buf = (struct strbuf *)ls->userData;
@@ -1988,29 +1591,22 @@ static void update_remote_info_refs(struct remote_lock *lock)
 static int remote_exists(const char *path)
 {
        char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
-       struct active_request_slot *slot;
-       struct slot_results results;
-       int ret = -1;
+       int ret;
 
        sprintf(url, "%s%s", repo->url, path);
 
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
-
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (results.http_code == 404)
-                       ret = 0;
-               else if (results.curl_result == CURLE_OK)
-                       ret = 1;
-               else
-                       fprintf(stderr, "HEAD HTTP error %ld\n", results.http_code);
-       } else {
-               fprintf(stderr, "Unable to start HEAD request\n");
+       switch (http_get_strbuf(url, NULL, 0)) {
+       case HTTP_OK:
+               ret = 1;
+               break;
+       case HTTP_MISSING_TARGET:
+               ret = 0;
+               break;
+       case HTTP_ERROR:
+               http_error(url, HTTP_ERROR);
+       default:
+               ret = -1;
        }
-
        free(url);
        return ret;
 }
@@ -2019,27 +1615,13 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
 {
        char *url;
        struct strbuf buffer = STRBUF_INIT;
-       struct active_request_slot *slot;
-       struct slot_results results;
 
        url = xmalloc(strlen(repo->url) + strlen(path) + 1);
        sprintf(url, "%s%s", repo->url, path);
 
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (results.curl_result != CURLE_OK) {
-                       die("Couldn't get %s for remote symref\n%s",
-                           url, curl_errorstr);
-               }
-       } else {
-               die("Unable to start remote symref request");
-       }
+       if (http_get_strbuf(url, &buffer, 0) != HTTP_OK)
+               die("Couldn't get %s for remote symref\n%s", url,
+                   curl_errorstr);
        free(url);
 
        free(*symref);
@@ -2120,13 +1702,13 @@ static int delete_remote_branch(char *pattern, int force)
                /* Remote HEAD must resolve to a known object */
                if (symref)
                        return error("Remote HEAD symrefs too deep");
-               if (is_zero_sha1(head_sha1))
+               if (is_null_sha1(head_sha1))
                        return error("Unable to resolve remote HEAD");
                if (!has_sha1_file(head_sha1))
                        return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", sha1_to_hex(head_sha1));
 
                /* Remote branch must resolve to a known object */
-               if (is_zero_sha1(remote_ref->old_sha1))
+               if (is_null_sha1(remote_ref->old_sha1))
                        return error("Unable to resolve remote branch %s",
                                     remote_ref->name);
                if (!has_sha1_file(remote_ref->old_sha1))
@@ -2168,6 +1750,25 @@ static int delete_remote_branch(char *pattern, int force)
        return 0;
 }
 
+void run_request_queue(void)
+{
+#ifdef USE_CURL_MULTI
+       is_running_queue = 1;
+       fill_active_slots();
+       add_fill_function(NULL, fill_active_slot);
+#endif
+       do {
+               finish_all_active_slots();
+#ifdef USE_CURL_MULTI
+               fill_active_slots();
+#endif
+       } while (request_queue_head && !aborted);
+
+#ifdef USE_CURL_MULTI
+       is_running_queue = 0;
+#endif
+}
+
 int main(int argc, char **argv)
 {
        struct transfer_request *request;
@@ -2212,6 +1813,7 @@ int main(int argc, char **argv)
                        }
                        if (!strcmp(arg, "--verbose")) {
                                push_verbosely = 1;
+                               http_is_verbose = 1;
                                continue;
                        }
                        if (!strcmp(arg, "-d")) {
@@ -2261,8 +1863,6 @@ int main(int argc, char **argv)
        remote->url[remote->url_nr++] = repo->url;
        http_init(remote);
 
-       no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
-
        if (repo->url && repo->url[strlen(repo->url)-1] != '/') {
                rewritten_url = xmalloc(strlen(repo->url)+2);
                strcpy(rewritten_url, repo->url);
@@ -2272,6 +1872,10 @@ int main(int argc, char **argv)
                repo->url = rewritten_url;
        }
 
+#ifdef USE_CURL_MULTI
+       is_running_queue = 0;
+#endif
+
        /* Verify DAV compliance/lock support */
        if (!locking_available()) {
                rc = 1;
@@ -2301,6 +1905,7 @@ int main(int argc, char **argv)
        local_refs = get_local_heads();
        fprintf(stderr, "Fetching remote heads...\n");
        get_dav_remote_heads();
+       run_request_queue();
 
        /* Remove a remote branch if -d or -D was specified */
        if (delete_branch) {
@@ -2311,9 +1916,7 @@ int main(int argc, char **argv)
        }
 
        /* match them up */
-       if (!remote_tail)
-               remote_tail = &remote_refs;
-       if (match_refs(local_refs, remote_refs, &remote_tail,
+       if (match_refs(local_refs, &remote_refs,
                       nr_refspec, (const char **) refspec, push_all)) {
                rc = -1;
                goto cleanup;
@@ -2327,14 +1930,14 @@ int main(int argc, char **argv)
        new_refs = 0;
        for (ref = remote_refs; ref; ref = ref->next) {
                char old_hex[60], *new_hex;
-               const char *commit_argv[4];
+               const char *commit_argv[5];
                int commit_argc;
                char *new_sha1_hex, *old_sha1_hex;
 
                if (!ref->peer_ref)
                        continue;
 
-               if (is_zero_sha1(ref->peer_ref->new_sha1)) {
+               if (is_null_sha1(ref->peer_ref->new_sha1)) {
                        if (delete_remote_branch(ref->name, 1) == -1) {
                                error("Could not remove %s", ref->name);
                                rc = -4;
@@ -2350,7 +1953,7 @@ int main(int argc, char **argv)
                }
 
                if (!force_all &&
-                   !is_zero_sha1(ref->old_sha1) &&
+                   !is_null_sha1(ref->old_sha1) &&
                    !ref->force) {
                        if (!has_sha1_file(ref->old_sha1) ||
                            !ref_newer(ref->peer_ref->new_sha1,
@@ -2400,13 +2003,14 @@ int main(int argc, char **argv)
                old_sha1_hex = NULL;
                commit_argv[1] = "--objects";
                commit_argv[2] = new_sha1_hex;
-               if (!push_all && !is_zero_sha1(ref->old_sha1)) {
+               if (!push_all && !is_null_sha1(ref->old_sha1)) {
                        old_sha1_hex = xmalloc(42);
                        sprintf(old_sha1_hex, "^%s",
                                sha1_to_hex(ref->old_sha1));
                        commit_argv[3] = old_sha1_hex;
                        commit_argc++;
                }
+               commit_argv[commit_argc] = NULL;
                init_revisions(&revs, setup_git_directory());
                setup_revisions(commit_argc, commit_argv, &revs, NULL);
                revs.edge_hint = 0; /* just in case */
@@ -2430,16 +2034,8 @@ int main(int argc, char **argv)
                if (objects_to_send)
                        fprintf(stderr, "    sending %d objects\n",
                                objects_to_send);
-#ifdef USE_CURL_MULTI
-               fill_active_slots();
-               add_fill_function(NULL, fill_active_slot);
-#endif
-               do {
-                       finish_all_active_slots();
-#ifdef USE_CURL_MULTI
-                       fill_active_slots();
-#endif
-               } while (request_queue_head && !aborted);
+
+               run_request_queue();
 
                /* Update the remote branch if all went well */
                if (aborted || !update_remote(ref->new_sha1, ref_lock))
@@ -2468,8 +2064,6 @@ int main(int argc, char **argv)
                unlock_remote(info_ref_lock);
        free(repo);
 
-       curl_slist_free_all(no_pragma_header);
-
        http_cleanup();
 
        request = request_queue_head;
index 7321ccc9fe751a1e608c6620bcce401eac7ff98c..700bc13112d65dfe8cf89af17522e28cf0d76e26 100644 (file)
@@ -1,12 +1,8 @@
 #include "cache.h"
 #include "commit.h"
-#include "pack.h"
 #include "walker.h"
 #include "http.h"
 
-#define PREV_BUF_SIZE 4096
-#define RANGE_HEADER_SIZE 30
-
 struct alt_base
 {
        char *base;
@@ -27,20 +23,8 @@ struct object_request
        struct walker *walker;
        unsigned char sha1[20];
        struct alt_base *repo;
-       char *url;
-       char filename[PATH_MAX];
-       char tmpfile[PATH_MAX];
-       int local;
        enum object_request_state state;
-       CURLcode curl_result;
-       char errorstr[CURL_ERROR_SIZE];
-       long http_code;
-       unsigned char real_sha1[20];
-       git_SHA_CTX c;
-       z_stream stream;
-       int zret;
-       int rename;
-       struct active_request_slot *slot;
+       struct http_object_request *req;
        struct object_request *next;
 };
 
@@ -57,39 +41,10 @@ struct walker_data {
        const char *url;
        int got_alternates;
        struct alt_base *alt;
-       struct curl_slist *no_pragma_header;
 };
 
 static struct object_request *object_queue_head;
 
-static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
-                              void *data)
-{
-       unsigned char expn[4096];
-       size_t size = eltsize * nmemb;
-       int posn = 0;
-       struct object_request *obj_req = (struct object_request *)data;
-       do {
-               ssize_t retval = xwrite(obj_req->local,
-                                      (char *) ptr + posn, size - posn);
-               if (retval < 0)
-                       return posn;
-               posn += retval;
-       } while (posn < size);
-
-       obj_req->stream.avail_in = size;
-       obj_req->stream.next_in = ptr;
-       do {
-               obj_req->stream.next_out = expn;
-               obj_req->stream.avail_out = sizeof(expn);
-               obj_req->zret = git_inflate(&obj_req->stream, Z_SYNC_FLUSH);
-               git_SHA1_Update(&obj_req->c, expn,
-                           sizeof(expn) - obj_req->stream.avail_out);
-       } while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
-       data_received++;
-       return size;
-}
-
 static void fetch_alternates(struct walker *walker, const char *base);
 
 static void process_object_response(void *callback_data);
@@ -97,165 +52,35 @@ static void process_object_response(void *callback_data);
 static void start_object_request(struct walker *walker,
                                 struct object_request *obj_req)
 {
-       char *hex = sha1_to_hex(obj_req->sha1);
-       char prevfile[PATH_MAX];
-       char *url;
-       char *posn;
-       int prevlocal;
-       unsigned char prev_buf[PREV_BUF_SIZE];
-       ssize_t prev_read = 0;
-       long prev_posn = 0;
-       char range[RANGE_HEADER_SIZE];
-       struct curl_slist *range_header = NULL;
        struct active_request_slot *slot;
-       struct walker_data *data = walker->data;
+       struct http_object_request *req;
 
-       snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
-       unlink_or_warn(prevfile);
-       rename(obj_req->tmpfile, prevfile);
-       unlink_or_warn(obj_req->tmpfile);
-
-       if (obj_req->local != -1)
-               error("fd leakage in start: %d", obj_req->local);
-       obj_req->local = open(obj_req->tmpfile,
-                             O_WRONLY | O_CREAT | O_EXCL, 0666);
-       /* This could have failed due to the "lazy directory creation";
-        * try to mkdir the last path component.
-        */
-       if (obj_req->local < 0 && errno == ENOENT) {
-               char *dir = strrchr(obj_req->tmpfile, '/');
-               if (dir) {
-                       *dir = 0;
-                       mkdir(obj_req->tmpfile, 0777);
-                       *dir = '/';
-               }
-               obj_req->local = open(obj_req->tmpfile,
-                                     O_WRONLY | O_CREAT | O_EXCL, 0666);
-       }
-
-       if (obj_req->local < 0) {
+       req = new_http_object_request(obj_req->repo->base, obj_req->sha1);
+       if (req == NULL) {
                obj_req->state = ABORTED;
-               error("Couldn't create temporary file %s for %s: %s",
-                     obj_req->tmpfile, obj_req->filename, strerror(errno));
                return;
        }
+       obj_req->req = req;
 
-       memset(&obj_req->stream, 0, sizeof(obj_req->stream));
-
-       git_inflate_init(&obj_req->stream);
-
-       git_SHA1_Init(&obj_req->c);
-
-       url = xmalloc(strlen(obj_req->repo->base) + 51);
-       obj_req->url = xmalloc(strlen(obj_req->repo->base) + 51);
-       strcpy(url, obj_req->repo->base);
-       posn = url + strlen(obj_req->repo->base);
-       strcpy(posn, "/objects/");
-       posn += 9;
-       memcpy(posn, hex, 2);
-       posn += 2;
-       *(posn++) = '/';
-       strcpy(posn, hex + 2);
-       strcpy(obj_req->url, url);
-
-       /* If a previous temp file is present, process what was already
-          fetched. */
-       prevlocal = open(prevfile, O_RDONLY);
-       if (prevlocal != -1) {
-               do {
-                       prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
-                       if (prev_read>0) {
-                               if (fwrite_sha1_file(prev_buf,
-                                                    1,
-                                                    prev_read,
-                                                    obj_req) == prev_read) {
-                                       prev_posn += prev_read;
-                               } else {
-                                       prev_read = -1;
-                               }
-                       }
-               } while (prev_read > 0);
-               close(prevlocal);
-       }
-       unlink_or_warn(prevfile);
-
-       /* Reset inflate/SHA1 if there was an error reading the previous temp
-          file; also rewind to the beginning of the local file. */
-       if (prev_read == -1) {
-               memset(&obj_req->stream, 0, sizeof(obj_req->stream));
-               git_inflate_init(&obj_req->stream);
-               git_SHA1_Init(&obj_req->c);
-               if (prev_posn>0) {
-                       prev_posn = 0;
-                       lseek(obj_req->local, 0, SEEK_SET);
-                       ftruncate(obj_req->local, 0);
-               }
-       }
-
-       slot = get_active_slot();
+       slot = req->slot;
        slot->callback_func = process_object_response;
        slot->callback_data = obj_req;
-       obj_req->slot = slot;
-
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
-       curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, data->no_pragma_header);
-
-       /* If we have successfully processed data from a previous fetch
-          attempt, only fetch the data we don't already have. */
-       if (prev_posn>0) {
-               if (walker->get_verbosely)
-                       fprintf(stderr,
-                               "Resuming fetch of object %s at byte %ld\n",
-                               hex, prev_posn);
-               sprintf(range, "Range: bytes=%ld-", prev_posn);
-               range_header = curl_slist_append(range_header, range);
-               curl_easy_setopt(slot->curl,
-                                CURLOPT_HTTPHEADER, range_header);
-       }
 
        /* Try to get the request started, abort the request on error */
        obj_req->state = ACTIVE;
        if (!start_active_slot(slot)) {
                obj_req->state = ABORTED;
-               obj_req->slot = NULL;
-               close(obj_req->local); obj_req->local = -1;
-               free(obj_req->url);
+               release_http_object_request(req);
                return;
        }
 }
 
 static void finish_object_request(struct object_request *obj_req)
 {
-       struct stat st;
-
-       close(obj_req->local); obj_req->local = -1;
-
-       if (obj_req->http_code == 416) {
-               fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
-       } else if (obj_req->curl_result != CURLE_OK) {
-               if (stat(obj_req->tmpfile, &st) == 0)
-                       if (st.st_size == 0)
-                               unlink_or_warn(obj_req->tmpfile);
-               return;
-       }
-
-       git_inflate_end(&obj_req->stream);
-       git_SHA1_Final(obj_req->real_sha1, &obj_req->c);
-       if (obj_req->zret != Z_STREAM_END) {
-               unlink_or_warn(obj_req->tmpfile);
+       if (finish_http_object_request(obj_req->req))
                return;
-       }
-       if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
-               unlink_or_warn(obj_req->tmpfile);
-               return;
-       }
-       obj_req->rename =
-               move_temp_to_file(obj_req->tmpfile, obj_req->filename);
 
-       if (obj_req->rename == 0)
+       if (obj_req->req->rename == 0)
                walker_say(obj_req->walker, "got %s\n", sha1_to_hex(obj_req->sha1));
 }
 
@@ -267,19 +92,16 @@ static void process_object_response(void *callback_data)
        struct walker_data *data = walker->data;
        struct alt_base *alt = data->alt;
 
-       obj_req->curl_result = obj_req->slot->curl_result;
-       obj_req->http_code = obj_req->slot->http_code;
-       obj_req->slot = NULL;
+       process_http_object_request(obj_req->req);
        obj_req->state = COMPLETE;
 
        /* Use alternates if necessary */
-       if (missing_target(obj_req)) {
+       if (missing_target(obj_req->req)) {
                fetch_alternates(walker, alt->base);
                if (obj_req->repo->next != NULL) {
                        obj_req->repo =
                                obj_req->repo->next;
-                       close(obj_req->local);
-                       obj_req->local = -1;
+                       release_http_object_request(obj_req->req);
                        start_object_request(walker, obj_req);
                        return;
                }
@@ -292,8 +114,8 @@ static void release_object_request(struct object_request *obj_req)
 {
        struct object_request *entry = object_queue_head;
 
-       if (obj_req->local != -1)
-               error("fd leakage in release: %d", obj_req->local);
+       if (obj_req->req !=NULL && obj_req->req->localfile != -1)
+               error("fd leakage in release: %d", obj_req->req->localfile);
        if (obj_req == object_queue_head) {
                object_queue_head = obj_req->next;
        } else {
@@ -303,7 +125,6 @@ static void release_object_request(struct object_request *obj_req)
                        entry->next = entry->next->next;
        }
 
-       free(obj_req->url);
        free(obj_req);
 }
 
@@ -331,28 +152,23 @@ static void prefetch(struct walker *walker, unsigned char *sha1)
        struct object_request *newreq;
        struct object_request *tail;
        struct walker_data *data = walker->data;
-       char *filename = sha1_file_name(sha1);
 
        newreq = xmalloc(sizeof(*newreq));
        newreq->walker = walker;
        hashcpy(newreq->sha1, sha1);
        newreq->repo = data->alt;
-       newreq->url = NULL;
-       newreq->local = -1;
        newreq->state = WAITING;
-       snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
-       snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
-                "%s.temp", filename);
-       newreq->slot = NULL;
+       newreq->req = NULL;
        newreq->next = NULL;
 
+       http_is_verbose = walker->get_verbosely;
+
        if (object_queue_head == NULL) {
                object_queue_head = newreq;
        } else {
                tail = object_queue_head;
-               while (tail->next != NULL) {
+               while (tail->next != NULL)
                        tail = tail->next;
-               }
                tail->next = newreq;
        }
 
@@ -362,92 +178,6 @@ static void prefetch(struct walker *walker, unsigned char *sha1)
 #endif
 }
 
-static int fetch_index(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
-{
-       char *hex = sha1_to_hex(sha1);
-       char *filename;
-       char *url;
-       char tmpfile[PATH_MAX];
-       long prev_posn = 0;
-       char range[RANGE_HEADER_SIZE];
-       struct curl_slist *range_header = NULL;
-       struct walker_data *data = walker->data;
-
-       FILE *indexfile;
-       struct active_request_slot *slot;
-       struct slot_results results;
-
-       if (has_pack_index(sha1))
-               return 0;
-
-       if (walker->get_verbosely)
-               fprintf(stderr, "Getting index for pack %s\n", hex);
-
-       url = xmalloc(strlen(repo->base) + 64);
-       sprintf(url, "%s/objects/pack/pack-%s.idx", repo->base, hex);
-
-       filename = sha1_pack_index_name(sha1);
-       snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
-       indexfile = fopen(tmpfile, "a");
-       if (!indexfile)
-               return error("Unable to open local file %s for pack index",
-                            tmpfile);
-
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, data->no_pragma_header);
-       slot->local = indexfile;
-
-       /* If there is data present from a previous transfer attempt,
-          resume where it left off */
-       prev_posn = ftell(indexfile);
-       if (prev_posn>0) {
-               if (walker->get_verbosely)
-                       fprintf(stderr,
-                               "Resuming fetch of index for pack %s at byte %ld\n",
-                               hex, prev_posn);
-               sprintf(range, "Range: bytes=%ld-", prev_posn);
-               range_header = curl_slist_append(range_header, range);
-               curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
-       }
-
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (results.curl_result != CURLE_OK) {
-                       fclose(indexfile);
-                       return error("Unable to get pack index %s\n%s", url,
-                                    curl_errorstr);
-               }
-       } else {
-               fclose(indexfile);
-               return error("Unable to start request");
-       }
-
-       fclose(indexfile);
-
-       return move_temp_to_file(tmpfile, filename);
-}
-
-static int setup_index(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
-{
-       struct packed_git *new_pack;
-       if (has_pack_file(sha1))
-               return 0; /* don't list this as something we can get */
-
-       if (fetch_index(walker, repo, sha1))
-               return -1;
-
-       new_pack = parse_pack_index(sha1);
-       if (!new_pack)
-               return -1; /* parse_pack_index() already issued error message */
-       new_pack->next = repo->packs;
-       repo->packs = new_pack;
-       return 0;
-}
-
 static void process_alternates_response(void *callback_data)
 {
        struct alternates_request *alt_req =
@@ -504,7 +234,8 @@ static void process_alternates_response(void *callback_data)
                        struct alt_base *newalt;
                        char *target = NULL;
                        if (data[i] == '/') {
-                               /* This counts
+                               /*
+                                * This counts
                                 * http://git.host/pub/scm/linux.git/
                                 * -----------here^
                                 * so memcpy(dst, base, serverlen) will
@@ -517,7 +248,8 @@ static void process_alternates_response(void *callback_data)
                                        okay = 1;
                                }
                        } else if (!memcmp(data + i, "../", 3)) {
-                               /* Relative URL; chop the corresponding
+                               /*
+                                * Relative URL; chop the corresponding
                                 * number of subpath from base (and ../
                                 * from data), and concatenate the result.
                                 *
@@ -546,7 +278,7 @@ static void process_alternates_response(void *callback_data)
                                }
                                /* If the server got removed, give up. */
                                okay = strchr(base, ':') - base + 3 <
-                                       serverlen;
+                                      serverlen;
                        } else if (alt_req->http_specific) {
                                char *colon = strchr(data + i, ':');
                                char *slash = strchr(data + i, '/');
@@ -590,9 +322,11 @@ static void fetch_alternates(struct walker *walker, const char *base)
        struct alternates_request alt_req;
        struct walker_data *cdata = walker->data;
 
-       /* If another request has already started fetching alternates,
-          wait for them to arrive and return to processing this request's
-          curl message */
+       /*
+        * If another request has already started fetching alternates,
+        * wait for them to arrive and return to processing this request's
+        * curl message
+        */
 #ifdef USE_CURL_MULTI
        while (cdata->got_alternates == 0) {
                step_active_slots();
@@ -612,8 +346,10 @@ static void fetch_alternates(struct walker *walker, const char *base)
        url = xmalloc(strlen(base) + 31);
        sprintf(url, "%s/objects/info/http-alternates", base);
 
-       /* Use a callback to process the result, since another request
-          may fail and need to have alternates loaded before continuing */
+       /*
+        * Use a callback to process the result, since another request
+        * may fail and need to have alternates loaded before continuing
+        */
        slot = get_active_slot();
        slot->callback_func = process_alternates_response;
        alt_req.walker = walker;
@@ -640,15 +376,7 @@ static void fetch_alternates(struct walker *walker, const char *base)
 
 static int fetch_indices(struct walker *walker, struct alt_base *repo)
 {
-       unsigned char sha1[20];
-       char *url;
-       struct strbuf buffer = STRBUF_INIT;
-       char *data;
-       int i = 0;
-       int ret = 0;
-
-       struct active_request_slot *slot;
-       struct slot_results results;
+       int ret;
 
        if (repo->got_indices)
                return 0;
@@ -656,76 +384,26 @@ static int fetch_indices(struct walker *walker, struct alt_base *repo)
        if (walker->get_verbosely)
                fprintf(stderr, "Getting pack list for %s\n", repo->base);
 
-       url = xmalloc(strlen(repo->base) + 21);
-       sprintf(url, "%s/objects/info/packs", repo->base);
-
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (results.curl_result != CURLE_OK) {
-                       if (missing_target(&results)) {
-                               repo->got_indices = 1;
-                               goto cleanup;
-                       } else {
-                               repo->got_indices = 0;
-                               ret = error("%s", curl_errorstr);
-                               goto cleanup;
-                       }
-               }
-       } else {
+       switch (http_get_info_packs(repo->base, &repo->packs)) {
+       case HTTP_OK:
+       case HTTP_MISSING_TARGET:
+               repo->got_indices = 1;
+               ret = 0;
+               break;
+       default:
                repo->got_indices = 0;
-               ret = error("Unable to start request");
-               goto cleanup;
+               ret = -1;
        }
 
-       data = buffer.buf;
-       while (i < buffer.len) {
-               switch (data[i]) {
-               case 'P':
-                       i++;
-                       if (i + 52 <= buffer.len &&
-                           !prefixcmp(data + i, " pack-") &&
-                           !prefixcmp(data + i + 46, ".pack\n")) {
-                               get_sha1_hex(data + i + 6, sha1);
-                               setup_index(walker, repo, sha1);
-                               i += 51;
-                               break;
-                       }
-               default:
-                       while (i < buffer.len && data[i] != '\n')
-                               i++;
-               }
-               i++;
-       }
-
-       repo->got_indices = 1;
-cleanup:
-       strbuf_release(&buffer);
-       free(url);
        return ret;
 }
 
 static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
 {
-       char *url;
        struct packed_git *target;
-       struct packed_git **lst;
-       FILE *packfile;
-       char *filename;
-       char tmpfile[PATH_MAX];
        int ret;
-       long prev_posn = 0;
-       char range[RANGE_HEADER_SIZE];
-       struct curl_slist *range_header = NULL;
-       struct walker_data *data = walker->data;
-
-       struct active_request_slot *slot;
        struct slot_results results;
+       struct http_pack_request *preq;
 
        if (fetch_indices(walker, repo))
                return -1;
@@ -740,80 +418,37 @@ static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned cha
                        sha1_to_hex(sha1));
        }
 
-       url = xmalloc(strlen(repo->base) + 65);
-       sprintf(url, "%s/objects/pack/pack-%s.pack",
-               repo->base, sha1_to_hex(target->sha1));
+       preq = new_http_pack_request(target, repo->base);
+       if (preq == NULL)
+               goto abort;
+       preq->lst = &repo->packs;
+       preq->slot->results = &results;
 
-       filename = sha1_pack_name(target->sha1);
-       snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
-       packfile = fopen(tmpfile, "a");
-       if (!packfile)
-               return error("Unable to open local file %s for pack",
-                            tmpfile);
-
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, data->no_pragma_header);
-       slot->local = packfile;
-
-       /* If there is data present from a previous transfer attempt,
-          resume where it left off */
-       prev_posn = ftell(packfile);
-       if (prev_posn>0) {
-               if (walker->get_verbosely)
-                       fprintf(stderr,
-                               "Resuming fetch of pack %s at byte %ld\n",
-                               sha1_to_hex(target->sha1), prev_posn);
-               sprintf(range, "Range: bytes=%ld-", prev_posn);
-               range_header = curl_slist_append(range_header, range);
-               curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
-       }
-
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
+       if (start_active_slot(preq->slot)) {
+               run_active_slot(preq->slot);
                if (results.curl_result != CURLE_OK) {
-                       fclose(packfile);
-                       return error("Unable to get pack file %s\n%s", url,
-                                    curl_errorstr);
+                       error("Unable to get pack file %s\n%s", preq->url,
+                             curl_errorstr);
+                       goto abort;
                }
        } else {
-               fclose(packfile);
-               return error("Unable to start request");
+               error("Unable to start request");
+               goto abort;
        }
 
-       target->pack_size = ftell(packfile);
-       fclose(packfile);
-
-       ret = move_temp_to_file(tmpfile, filename);
+       ret = finish_http_pack_request(preq);
+       release_http_pack_request(preq);
        if (ret)
                return ret;
 
-       lst = &repo->packs;
-       while (*lst != target)
-               lst = &((*lst)->next);
-       *lst = (*lst)->next;
-
-       if (verify_pack(target))
-               return -1;
-       install_packed_git(target);
-
        return 0;
+
+abort:
+       return -1;
 }
 
 static void abort_object_request(struct object_request *obj_req)
 {
-       if (obj_req->local >= 0) {
-               close(obj_req->local);
-               obj_req->local = -1;
-       }
-       unlink_or_warn(obj_req->tmpfile);
-       if (obj_req->slot) {
-               release_active_slot(obj_req->slot);
-               obj_req->slot = NULL;
-       }
        release_object_request(obj_req);
 }
 
@@ -822,6 +457,7 @@ static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned c
        char *hex = sha1_to_hex(sha1);
        int ret = 0;
        struct object_request *obj_req = object_queue_head;
+       struct http_object_request *req;
 
        while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
                obj_req = obj_req->next;
@@ -829,45 +465,55 @@ static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned c
                return error("Couldn't find request for %s in the queue", hex);
 
        if (has_sha1_file(obj_req->sha1)) {
+               if (obj_req->req != NULL)
+                       abort_http_object_request(obj_req->req);
                abort_object_request(obj_req);
                return 0;
        }
 
 #ifdef USE_CURL_MULTI
-       while (obj_req->state == WAITING) {
+       while (obj_req->state == WAITING)
                step_active_slots();
-       }
 #else
        start_object_request(walker, obj_req);
 #endif
 
-       while (obj_req->state == ACTIVE) {
-               run_active_slot(obj_req->slot);
-       }
-       if (obj_req->local != -1) {
-               close(obj_req->local); obj_req->local = -1;
+       /*
+        * obj_req->req might change when fetching alternates in the callback
+        * process_object_response; therefore, the "shortcut" variable, req,
+        * is used only after we're done with slots.
+        */
+       while (obj_req->state == ACTIVE)
+               run_active_slot(obj_req->req->slot);
+
+       req = obj_req->req;
+
+       if (req->localfile != -1) {
+               close(req->localfile);
+               req->localfile = -1;
        }
 
        if (obj_req->state == ABORTED) {
                ret = error("Request for %s aborted", hex);
-       } else if (obj_req->curl_result != CURLE_OK &&
-                  obj_req->http_code != 416) {
-               if (missing_target(obj_req))
+       } else if (req->curl_result != CURLE_OK &&
+                  req->http_code != 416) {
+               if (missing_target(req))
                        ret = -1; /* Be silent, it is probably in a pack. */
                else
                        ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
-                                   obj_req->errorstr, obj_req->curl_result,
-                                   obj_req->http_code, hex);
-       } else if (obj_req->zret != Z_STREAM_END) {
+                                   req->errorstr, req->curl_result,
+                                   req->http_code, hex);
+       } else if (req->zret != Z_STREAM_END) {
                walker->corrupt_object_found++;
-               ret = error("File %s (%s) corrupt", hex, obj_req->url);
-       } else if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
+               ret = error("File %s (%s) corrupt", hex, req->url);
+       } else if (hashcmp(obj_req->sha1, req->real_sha1)) {
                ret = error("File %s has bad hash", hex);
-       } else if (obj_req->rename < 0) {
+       } else if (req->rename < 0) {
                ret = error("unable to write sha1 filename %s",
-                           obj_req->filename);
+                           req->filename);
        }
 
+       release_http_object_request(req);
        release_object_request(obj_req);
        return ret;
 }
@@ -897,10 +543,7 @@ static int fetch_ref(struct walker *walker, struct ref *ref)
 
 static void cleanup(struct walker *walker)
 {
-       struct walker_data *data = walker->data;
        http_cleanup();
-
-       curl_slist_free_all(data->no_pragma_header);
 }
 
 struct walker *get_http_walker(const char *url, struct remote *remote)
@@ -911,8 +554,6 @@ struct walker *get_http_walker(const char *url, struct remote *remote)
 
        http_init(remote);
 
-       data->no_pragma_header = curl_slist_append(NULL, "Pragma:");
-
        data->alt = xmalloc(sizeof(*data->alt));
        data->alt->base = xmalloc(strlen(url) + 1);
        strcpy(data->alt->base, url);
diff --git a/http.c b/http.c
index 2e3d6493ef40e1b34fea8d166d760f0b1fcccafc..b0499482455e256a3c2c9b6498b21c32d328c5c9 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,7 +1,9 @@
 #include "http.h"
+#include "pack.h"
 
 int data_received;
 int active_requests;
+int http_is_verbose;
 
 #ifdef USE_CURL_MULTI
 static int max_requests = -1;
@@ -10,11 +12,15 @@ static CURLM *curlm;
 #ifndef NO_CURL_EASY_DUPHANDLE
 static CURL *curl_default;
 #endif
+
+#define PREV_BUF_SIZE 4096
+#define RANGE_HEADER_SIZE 30
+
 char curl_errorstr[CURL_ERROR_SIZE];
 
 static int curl_ssl_verify = -1;
 static const char *ssl_cert;
-#if LIBCURL_VERSION_NUM >= 0x070902
+#if LIBCURL_VERSION_NUM >= 0x070903
 static const char *ssl_key;
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
@@ -28,6 +34,7 @@ static const char *curl_http_proxy;
 static char *user_name, *user_pass;
 
 static struct curl_slist *pragma_header;
+static struct curl_slist *no_pragma_header;
 
 static struct active_request_slot *active_queue_head;
 
@@ -119,7 +126,7 @@ static int http_options(const char *var, const char *value, void *cb)
        }
        if (!strcmp("http.sslcert", var))
                return git_config_string(&ssl_cert, var, value);
-#if LIBCURL_VERSION_NUM >= 0x070902
+#if LIBCURL_VERSION_NUM >= 0x070903
        if (!strcmp("http.sslkey", var))
                return git_config_string(&ssl_key, var, value);
 #endif
@@ -189,7 +196,7 @@ static CURL *get_curl_handle(void)
 
        if (ssl_cert != NULL)
                curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
-#if LIBCURL_VERSION_NUM >= 0x070902
+#if LIBCURL_VERSION_NUM >= 0x070903
        if (ssl_key != NULL)
                curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
 #endif
@@ -276,6 +283,8 @@ void http_init(struct remote *remote)
        char *low_speed_limit;
        char *low_speed_time;
 
+       http_is_verbose = 0;
+
        git_config(http_options, NULL);
 
        curl_global_init(CURL_GLOBAL_ALL);
@@ -284,6 +293,7 @@ void http_init(struct remote *remote)
                curl_http_proxy = xstrdup(remote->http_proxy);
 
        pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
+       no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
 
 #ifdef USE_CURL_MULTI
        {
@@ -303,7 +313,7 @@ void http_init(struct remote *remote)
                curl_ssl_verify = 0;
 
        set_from_env(&ssl_cert, "GIT_SSL_CERT");
-#if LIBCURL_VERSION_NUM >= 0x070902
+#if LIBCURL_VERSION_NUM >= 0x070903
        set_from_env(&ssl_key, "GIT_SSL_KEY");
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
@@ -366,6 +376,9 @@ void http_cleanup(void)
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
 
+       curl_slist_free_all(no_pragma_header);
+       no_pragma_header = NULL;
+
        if (curl_http_proxy) {
                free((void *)curl_http_proxy);
                curl_http_proxy = NULL;
@@ -611,6 +624,7 @@ void finish_all_active_slots(void)
                }
 }
 
+/* Helpers for modifying and creating URLs */
 static inline int needs_quote(int ch)
 {
        if (((ch >= 'A') && (ch <= 'Z'))
@@ -631,15 +645,20 @@ static inline int hex(int v)
                return 'A' + v - 10;
 }
 
+static void end_url_with_slash(struct strbuf *buf, const char *url)
+{
+       strbuf_addstr(buf, url);
+       if (buf->len && buf->buf[buf->len - 1] != '/')
+               strbuf_addstr(buf, "/");
+}
+
 static char *quote_ref_url(const char *base, const char *ref)
 {
        struct strbuf buf = STRBUF_INIT;
        const char *cp;
        int ch;
 
-       strbuf_addstr(&buf, base);
-       if (buf.len && buf.buf[buf.len - 1] != '/' && *ref != '/')
-               strbuf_addstr(&buf, "/");
+       end_url_with_slash(&buf, base);
 
        for (cp = ref; (ch = *cp) != 0; cp++)
                if (needs_quote(ch))
@@ -650,41 +669,575 @@ static char *quote_ref_url(const char *base, const char *ref)
        return strbuf_detach(&buf, NULL);
 }
 
-int http_fetch_ref(const char *base, struct ref *ref)
+void append_remote_object_url(struct strbuf *buf, const char *url,
+                             const char *hex,
+                             int only_two_digit_prefix)
+{
+       strbuf_addf(buf, "%s/objects/%.*s/", url, 2, hex);
+       if (!only_two_digit_prefix)
+               strbuf_addf(buf, "%s", hex+2);
+}
+
+char *get_remote_object_url(const char *url, const char *hex,
+                           int only_two_digit_prefix)
+{
+       struct strbuf buf = STRBUF_INIT;
+       append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
+       return strbuf_detach(&buf, NULL);
+}
+
+/* http_request() targets */
+#define HTTP_REQUEST_STRBUF    0
+#define HTTP_REQUEST_FILE      1
+
+static int http_request(const char *url, void *result, int target, int options)
 {
-       char *url;
-       struct strbuf buffer = STRBUF_INIT;
        struct active_request_slot *slot;
        struct slot_results results;
+       struct curl_slist *headers = NULL;
+       struct strbuf buf = STRBUF_INIT;
        int ret;
 
-       url = quote_ref_url(base, ref->name);
        slot = get_active_slot();
        slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+
+       if (result == NULL) {
+               curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
+       } else {
+               curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
+               curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
+
+               if (target == HTTP_REQUEST_FILE) {
+                       long posn = ftell(result);
+                       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+                                        fwrite);
+                       if (posn > 0) {
+                               strbuf_addf(&buf, "Range: bytes=%ld-", posn);
+                               headers = curl_slist_append(headers, buf.buf);
+                               strbuf_reset(&buf);
+                       }
+                       slot->local = result;
+               } else
+                       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+                                        fwrite_buffer);
+       }
+
+       strbuf_addstr(&buf, "Pragma:");
+       if (options & HTTP_NO_CACHE)
+               strbuf_addstr(&buf, " no-cache");
+
+       headers = curl_slist_append(headers, buf.buf);
+
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
+
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (results.curl_result == CURLE_OK) {
-                       strbuf_rtrim(&buffer);
-                       if (buffer.len == 40)
-                               ret = get_sha1_hex(buffer.buf, ref->old_sha1);
-                       else if (!prefixcmp(buffer.buf, "ref: ")) {
-                               ref->symref = xstrdup(buffer.buf + 5);
-                               ret = 0;
-                       } else
-                               ret = 1;
-               } else {
-                       ret = error("Couldn't get %s for %s\n%s",
-                                   url, ref->name, curl_errorstr);
-               }
+               if (results.curl_result == CURLE_OK)
+                       ret = HTTP_OK;
+               else if (missing_target(&results))
+                       ret = HTTP_MISSING_TARGET;
+               else
+                       ret = HTTP_ERROR;
        } else {
-               ret = error("Unable to start request");
+               error("Unable to start HTTP request for %s", url);
+               ret = HTTP_START_FAILED;
+       }
+
+       slot->local = NULL;
+       curl_slist_free_all(headers);
+       strbuf_release(&buf);
+
+       return ret;
+}
+
+int http_get_strbuf(const char *url, struct strbuf *result, int options)
+{
+       return http_request(url, result, HTTP_REQUEST_STRBUF, options);
+}
+
+int http_get_file(const char *url, const char *filename, int options)
+{
+       int ret;
+       struct strbuf tmpfile = STRBUF_INIT;
+       FILE *result;
+
+       strbuf_addf(&tmpfile, "%s.temp", filename);
+       result = fopen(tmpfile.buf, "a");
+       if (! result) {
+               error("Unable to open local file %s", tmpfile.buf);
+               ret = HTTP_ERROR;
+               goto cleanup;
+       }
+
+       ret = http_request(url, result, HTTP_REQUEST_FILE, options);
+       fclose(result);
+
+       if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
+               ret = HTTP_ERROR;
+cleanup:
+       strbuf_release(&tmpfile);
+       return ret;
+}
+
+int http_error(const char *url, int ret)
+{
+       /* http_request has already handled HTTP_START_FAILED. */
+       if (ret != HTTP_START_FAILED)
+               error("%s while accessing %s\n", curl_errorstr, url);
+
+       return ret;
+}
+
+int http_fetch_ref(const char *base, struct ref *ref)
+{
+       char *url;
+       struct strbuf buffer = STRBUF_INIT;
+       int ret = -1;
+
+       url = quote_ref_url(base, ref->name);
+       if (http_get_strbuf(url, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
+               strbuf_rtrim(&buffer);
+               if (buffer.len == 40)
+                       ret = get_sha1_hex(buffer.buf, ref->old_sha1);
+               else if (!prefixcmp(buffer.buf, "ref: ")) {
+                       ref->symref = xstrdup(buffer.buf + 5);
+                       ret = 0;
+               }
        }
 
        strbuf_release(&buffer);
        free(url);
        return ret;
 }
+
+/* Helpers for fetching packs */
+static int fetch_pack_index(unsigned char *sha1, const char *base_url)
+{
+       int ret = 0;
+       char *hex = xstrdup(sha1_to_hex(sha1));
+       char *filename;
+       char *url;
+       struct strbuf buf = STRBUF_INIT;
+
+       /* Don't use the index if the pack isn't there */
+       end_url_with_slash(&buf, base_url);
+       strbuf_addf(&buf, "objects/pack/pack-%s.pack", hex);
+       url = strbuf_detach(&buf, 0);
+
+       if (http_get_strbuf(url, NULL, 0)) {
+               ret = error("Unable to verify pack %s is available",
+                           hex);
+               goto cleanup;
+       }
+
+       if (has_pack_index(sha1)) {
+               ret = 0;
+               goto cleanup;
+       }
+
+       if (http_is_verbose)
+               fprintf(stderr, "Getting index for pack %s\n", hex);
+
+       end_url_with_slash(&buf, base_url);
+       strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
+       url = strbuf_detach(&buf, NULL);
+
+       filename = sha1_pack_index_name(sha1);
+       if (http_get_file(url, filename, 0) != HTTP_OK)
+               ret = error("Unable to get pack index %s\n", url);
+
+cleanup:
+       free(hex);
+       free(url);
+       return ret;
+}
+
+static int fetch_and_setup_pack_index(struct packed_git **packs_head,
+       unsigned char *sha1, const char *base_url)
+{
+       struct packed_git *new_pack;
+
+       if (fetch_pack_index(sha1, base_url))
+               return -1;
+
+       new_pack = parse_pack_index(sha1);
+       if (!new_pack)
+               return -1; /* parse_pack_index() already issued error message */
+       new_pack->next = *packs_head;
+       *packs_head = new_pack;
+       return 0;
+}
+
+int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
+{
+       int ret = 0, i = 0;
+       char *url, *data;
+       struct strbuf buf = STRBUF_INIT;
+       unsigned char sha1[20];
+
+       end_url_with_slash(&buf, base_url);
+       strbuf_addstr(&buf, "objects/info/packs");
+       url = strbuf_detach(&buf, NULL);
+
+       ret = http_get_strbuf(url, &buf, HTTP_NO_CACHE);
+       if (ret != HTTP_OK)
+               goto cleanup;
+
+       data = buf.buf;
+       while (i < buf.len) {
+               switch (data[i]) {
+               case 'P':
+                       i++;
+                       if (i + 52 <= buf.len &&
+                           !prefixcmp(data + i, " pack-") &&
+                           !prefixcmp(data + i + 46, ".pack\n")) {
+                               get_sha1_hex(data + i + 6, sha1);
+                               fetch_and_setup_pack_index(packs_head, sha1,
+                                                     base_url);
+                               i += 51;
+                               break;
+                       }
+               default:
+                       while (i < buf.len && data[i] != '\n')
+                               i++;
+               }
+               i++;
+       }
+
+cleanup:
+       free(url);
+       return ret;
+}
+
+void release_http_pack_request(struct http_pack_request *preq)
+{
+       if (preq->packfile != NULL) {
+               fclose(preq->packfile);
+               preq->packfile = NULL;
+               preq->slot->local = NULL;
+       }
+       if (preq->range_header != NULL) {
+               curl_slist_free_all(preq->range_header);
+               preq->range_header = NULL;
+       }
+       preq->slot = NULL;
+       free(preq->url);
+}
+
+int finish_http_pack_request(struct http_pack_request *preq)
+{
+       int ret;
+       struct packed_git **lst;
+
+       preq->target->pack_size = ftell(preq->packfile);
+
+       if (preq->packfile != NULL) {
+               fclose(preq->packfile);
+               preq->packfile = NULL;
+               preq->slot->local = NULL;
+       }
+
+       ret = move_temp_to_file(preq->tmpfile, preq->filename);
+       if (ret)
+               return ret;
+
+       lst = preq->lst;
+       while (*lst != preq->target)
+               lst = &((*lst)->next);
+       *lst = (*lst)->next;
+
+       if (verify_pack(preq->target))
+               return -1;
+       install_packed_git(preq->target);
+
+       return 0;
+}
+
+struct http_pack_request *new_http_pack_request(
+       struct packed_git *target, const char *base_url)
+{
+       char *url;
+       char *filename;
+       long prev_posn = 0;
+       char range[RANGE_HEADER_SIZE];
+       struct strbuf buf = STRBUF_INIT;
+       struct http_pack_request *preq;
+
+       preq = xmalloc(sizeof(*preq));
+       preq->target = target;
+       preq->range_header = NULL;
+
+       end_url_with_slash(&buf, base_url);
+       strbuf_addf(&buf, "objects/pack/pack-%s.pack",
+               sha1_to_hex(target->sha1));
+       url = strbuf_detach(&buf, NULL);
+       preq->url = xstrdup(url);
+
+       filename = sha1_pack_name(target->sha1);
+       snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
+       snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
+       preq->packfile = fopen(preq->tmpfile, "a");
+       if (!preq->packfile) {
+               error("Unable to open local file %s for pack",
+                     preq->tmpfile);
+               goto abort;
+       }
+
+       preq->slot = get_active_slot();
+       preq->slot->local = preq->packfile;
+       curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
+       curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
+       curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url);
+       curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
+               no_pragma_header);
+
+       /*
+        * If there is data present from a previous transfer attempt,
+        * resume where it left off
+        */
+       prev_posn = ftell(preq->packfile);
+       if (prev_posn>0) {
+               if (http_is_verbose)
+                       fprintf(stderr,
+                               "Resuming fetch of pack %s at byte %ld\n",
+                               sha1_to_hex(target->sha1), prev_posn);
+               sprintf(range, "Range: bytes=%ld-", prev_posn);
+               preq->range_header = curl_slist_append(NULL, range);
+               curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
+                       preq->range_header);
+       }
+
+       return preq;
+
+abort:
+       free(filename);
+       return NULL;
+}
+
+/* Helpers for fetching objects (loose) */
+static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
+                              void *data)
+{
+       unsigned char expn[4096];
+       size_t size = eltsize * nmemb;
+       int posn = 0;
+       struct http_object_request *freq =
+               (struct http_object_request *)data;
+       do {
+               ssize_t retval = xwrite(freq->localfile,
+                                       (char *) ptr + posn, size - posn);
+               if (retval < 0)
+                       return posn;
+               posn += retval;
+       } while (posn < size);
+
+       freq->stream.avail_in = size;
+       freq->stream.next_in = ptr;
+       do {
+               freq->stream.next_out = expn;
+               freq->stream.avail_out = sizeof(expn);
+               freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
+               git_SHA1_Update(&freq->c, expn,
+                               sizeof(expn) - freq->stream.avail_out);
+       } while (freq->stream.avail_in && freq->zret == Z_OK);
+       data_received++;
+       return size;
+}
+
+struct http_object_request *new_http_object_request(const char *base_url,
+       unsigned char *sha1)
+{
+       char *hex = sha1_to_hex(sha1);
+       char *filename;
+       char prevfile[PATH_MAX];
+       char *url;
+       int prevlocal;
+       unsigned char prev_buf[PREV_BUF_SIZE];
+       ssize_t prev_read = 0;
+       long prev_posn = 0;
+       char range[RANGE_HEADER_SIZE];
+       struct curl_slist *range_header = NULL;
+       struct http_object_request *freq;
+
+       freq = xmalloc(sizeof(*freq));
+       hashcpy(freq->sha1, sha1);
+       freq->localfile = -1;
+
+       filename = sha1_file_name(sha1);
+       snprintf(freq->filename, sizeof(freq->filename), "%s", filename);
+       snprintf(freq->tmpfile, sizeof(freq->tmpfile),
+                "%s.temp", filename);
+
+       snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
+       unlink_or_warn(prevfile);
+       rename(freq->tmpfile, prevfile);
+       unlink_or_warn(freq->tmpfile);
+
+       if (freq->localfile != -1)
+               error("fd leakage in start: %d", freq->localfile);
+       freq->localfile = open(freq->tmpfile,
+                              O_WRONLY | O_CREAT | O_EXCL, 0666);
+       /*
+        * This could have failed due to the "lazy directory creation";
+        * try to mkdir the last path component.
+        */
+       if (freq->localfile < 0 && errno == ENOENT) {
+               char *dir = strrchr(freq->tmpfile, '/');
+               if (dir) {
+                       *dir = 0;
+                       mkdir(freq->tmpfile, 0777);
+                       *dir = '/';
+               }
+               freq->localfile = open(freq->tmpfile,
+                                      O_WRONLY | O_CREAT | O_EXCL, 0666);
+       }
+
+       if (freq->localfile < 0) {
+               error("Couldn't create temporary file %s for %s: %s",
+                     freq->tmpfile, freq->filename, strerror(errno));
+               goto abort;
+       }
+
+       memset(&freq->stream, 0, sizeof(freq->stream));
+
+       git_inflate_init(&freq->stream);
+
+       git_SHA1_Init(&freq->c);
+
+       url = get_remote_object_url(base_url, hex, 0);
+       freq->url = xstrdup(url);
+
+       /*
+        * If a previous temp file is present, process what was already
+        * fetched.
+        */
+       prevlocal = open(prevfile, O_RDONLY);
+       if (prevlocal != -1) {
+               do {
+                       prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
+                       if (prev_read>0) {
+                               if (fwrite_sha1_file(prev_buf,
+                                                    1,
+                                                    prev_read,
+                                                    freq) == prev_read) {
+                                       prev_posn += prev_read;
+                               } else {
+                                       prev_read = -1;
+                               }
+                       }
+               } while (prev_read > 0);
+               close(prevlocal);
+       }
+       unlink_or_warn(prevfile);
+
+       /*
+        * Reset inflate/SHA1 if there was an error reading the previous temp
+        * file; also rewind to the beginning of the local file.
+        */
+       if (prev_read == -1) {
+               memset(&freq->stream, 0, sizeof(freq->stream));
+               git_inflate_init(&freq->stream);
+               git_SHA1_Init(&freq->c);
+               if (prev_posn>0) {
+                       prev_posn = 0;
+                       lseek(freq->localfile, 0, SEEK_SET);
+                       ftruncate(freq->localfile, 0);
+               }
+       }
+
+       freq->slot = get_active_slot();
+
+       curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
+       curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
+       curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
+       curl_easy_setopt(freq->slot->curl, CURLOPT_URL, url);
+       curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+
+       /*
+        * If we have successfully processed data from a previous fetch
+        * attempt, only fetch the data we don't already have.
+        */
+       if (prev_posn>0) {
+               if (http_is_verbose)
+                       fprintf(stderr,
+                               "Resuming fetch of object %s at byte %ld\n",
+                               hex, prev_posn);
+               sprintf(range, "Range: bytes=%ld-", prev_posn);
+               range_header = curl_slist_append(range_header, range);
+               curl_easy_setopt(freq->slot->curl,
+                                CURLOPT_HTTPHEADER, range_header);
+       }
+
+       return freq;
+
+       free(url);
+abort:
+       free(filename);
+       free(freq);
+       return NULL;
+}
+
+void process_http_object_request(struct http_object_request *freq)
+{
+       if (freq->slot == NULL)
+               return;
+       freq->curl_result = freq->slot->curl_result;
+       freq->http_code = freq->slot->http_code;
+       freq->slot = NULL;
+}
+
+int finish_http_object_request(struct http_object_request *freq)
+{
+       struct stat st;
+
+       close(freq->localfile);
+       freq->localfile = -1;
+
+       process_http_object_request(freq);
+
+       if (freq->http_code == 416) {
+               fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
+       } else if (freq->curl_result != CURLE_OK) {
+               if (stat(freq->tmpfile, &st) == 0)
+                       if (st.st_size == 0)
+                               unlink_or_warn(freq->tmpfile);
+               return -1;
+       }
+
+       git_inflate_end(&freq->stream);
+       git_SHA1_Final(freq->real_sha1, &freq->c);
+       if (freq->zret != Z_STREAM_END) {
+               unlink_or_warn(freq->tmpfile);
+               return -1;
+       }
+       if (hashcmp(freq->sha1, freq->real_sha1)) {
+               unlink_or_warn(freq->tmpfile);
+               return -1;
+       }
+       freq->rename =
+               move_temp_to_file(freq->tmpfile, freq->filename);
+
+       return freq->rename;
+}
+
+void abort_http_object_request(struct http_object_request *freq)
+{
+       unlink_or_warn(freq->tmpfile);
+
+       release_http_object_request(freq);
+}
+
+void release_http_object_request(struct http_object_request *freq)
+{
+       if (freq->localfile != -1) {
+               close(freq->localfile);
+               freq->localfile = -1;
+       }
+       if (freq->url != NULL) {
+               free(freq->url);
+               freq->url = NULL;
+       }
+       freq->slot = NULL;
+}
diff --git a/http.h b/http.h
index 26abebed1f35db971e423f79c433bed8c5338789..4c4e99c2f6cae97110efe28986333fc5cbc4c095 100644 (file)
--- a/http.h
+++ b/http.h
@@ -93,6 +93,7 @@ extern void http_cleanup(void);
 
 extern int data_received;
 extern int active_requests;
+extern int http_is_verbose;
 
 extern char curl_errorstr[CURL_ERROR_SIZE];
 
@@ -109,6 +110,90 @@ static inline int missing__target(int code, int result)
 
 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
 
+/* Helpers for modifying and creating URLs */
+extern void append_remote_object_url(struct strbuf *buf, const char *url,
+                                    const char *hex,
+                                    int only_two_digit_prefix);
+extern char *get_remote_object_url(const char *url, const char *hex,
+                                  int only_two_digit_prefix);
+
+/* Options for http_request_*() */
+#define HTTP_NO_CACHE          1
+
+/* Return values for http_request_*() */
+#define HTTP_OK                        0
+#define HTTP_MISSING_TARGET    1
+#define HTTP_ERROR             2
+#define HTTP_START_FAILED      3
+
+/*
+ * Requests an url and stores the result in a strbuf.
+ *
+ * If the result pointer is NULL, a HTTP HEAD request is made instead of GET.
+ */
+int http_get_strbuf(const char *url, struct strbuf *result, int options);
+
+/*
+ * Downloads an url and stores the result in the given file.
+ *
+ * If a previous interrupted download is detected (i.e. a previous temporary
+ * file is still around) the download is resumed.
+ */
+int http_get_file(const char *url, const char *filename, int options);
+
+/*
+ * Prints an error message using error() containing url and curl_errorstr,
+ * and returns ret.
+ */
+int http_error(const char *url, int ret);
+
 extern int http_fetch_ref(const char *base, struct ref *ref);
 
+/* Helpers for fetching packs */
+extern int http_get_info_packs(const char *base_url,
+       struct packed_git **packs_head);
+
+struct http_pack_request
+{
+       char *url;
+       struct packed_git *target;
+       struct packed_git **lst;
+       FILE *packfile;
+       char filename[PATH_MAX];
+       char tmpfile[PATH_MAX];
+       struct curl_slist *range_header;
+       struct active_request_slot *slot;
+};
+
+extern struct http_pack_request *new_http_pack_request(
+       struct packed_git *target, const char *base_url);
+extern int finish_http_pack_request(struct http_pack_request *preq);
+extern void release_http_pack_request(struct http_pack_request *preq);
+
+/* Helpers for fetching object */
+struct http_object_request
+{
+       char *url;
+       char filename[PATH_MAX];
+       char tmpfile[PATH_MAX];
+       int localfile;
+       CURLcode curl_result;
+       char errorstr[CURL_ERROR_SIZE];
+       long http_code;
+       unsigned char sha1[20];
+       unsigned char real_sha1[20];
+       git_SHA_CTX c;
+       z_stream stream;
+       int zret;
+       int rename;
+       struct active_request_slot *slot;
+};
+
+extern struct http_object_request *new_http_object_request(
+       const char *base_url, unsigned char *sha1);
+extern void process_http_object_request(struct http_object_request *freq);
+extern int finish_http_object_request(struct http_object_request *freq);
+extern void abort_http_object_request(struct http_object_request *freq);
+extern void release_http_object_request(struct http_object_request *freq);
+
 #endif /* HTTP_H */
index 8154cb2116da9257ecf286c46f77fc1ed2a62afc..e4c83b9d5b989671c1accc573235479ad77a5ab7 100644 (file)
@@ -982,9 +982,7 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
        struct imap_store *ctx;
        struct imap *imap;
        char *arg, *rsp;
-       struct hostent *he;
-       struct sockaddr_in addr;
-       int s, a[2], preauth;
+       int s = -1, a[2], preauth;
        pid_t pid;
 
        ctx = xcalloc(sizeof(*ctx), 1);
@@ -1021,6 +1019,51 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 
                imap_info("ok\n");
        } else {
+#ifndef NO_IPV6
+               struct addrinfo hints, *ai0, *ai;
+               int gai;
+               char portstr[6];
+
+               snprintf(portstr, sizeof(portstr), "%hu", srvc->port);
+
+               memset(&hints, 0, sizeof(hints));
+               hints.ai_socktype = SOCK_STREAM;
+               hints.ai_protocol = IPPROTO_TCP;
+
+               imap_info("Resolving %s... ", srvc->host);
+               gai = getaddrinfo(srvc->host, portstr, &hints, &ai);
+               if (gai) {
+                       fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
+                       goto bail;
+               }
+               imap_info("ok\n");
+
+               for (ai0 = ai; ai; ai = ai->ai_next) {
+                       char addr[NI_MAXHOST];
+
+                       s = socket(ai->ai_family, ai->ai_socktype,
+                                  ai->ai_protocol);
+                       if (s < 0)
+                               continue;
+
+                       getnameinfo(ai->ai_addr, ai->ai_addrlen, addr,
+                                   sizeof(addr), NULL, 0, NI_NUMERICHOST);
+                       imap_info("Connecting to [%s]:%s... ", addr, portstr);
+
+                       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
+                               close(s);
+                               s = -1;
+                               perror("connect");
+                               continue;
+                       }
+
+                       break;
+               }
+               freeaddrinfo(ai0);
+#else /* NO_IPV6 */
+               struct hostent *he;
+               struct sockaddr_in addr;
+
                memset(&addr, 0, sizeof(addr));
                addr.sin_port = htons(srvc->port);
                addr.sin_family = AF_INET;
@@ -1040,7 +1083,12 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
                imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
                if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
                        close(s);
+                       s = -1;
                        perror("connect");
+               }
+#endif
+               if (s < 0) {
+                       fputs("Error: unable to connect to server.\n", stderr);
                        goto bail;
                }
 
index 6e93ee6af64593937ee9b078e599e81d40b74303..0c92bafcbb80cc539de9a86305562ff291361db9 100644 (file)
@@ -293,8 +293,8 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size)
 
 static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
 {
-       unsigned char *p, c;
-       unsigned long size;
+       unsigned char *p;
+       unsigned long size, c;
        off_t base_offset;
        unsigned shift;
        void *data;
@@ -312,7 +312,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
                p = fill(1);
                c = *p;
                use(1);
-               size += (c & 0x7fUL) << shift;
+               size += (c & 0x7f) << shift;
                shift += 7;
        }
        obj->size = size;
index 81c02ad0531e98379d8cd11dc7c2d70214d67fb4..9168958e863e50f0d1bd1446b3314cb3dd2e772f 100644 (file)
@@ -175,8 +175,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
                { "B", temp[2] },
                { NULL }
        };
-       struct child_process child;
-       const char *args[20];
+       const char *args[] = { "sh", "-c", NULL, NULL };
        int status, fd, i;
        struct stat st;
 
@@ -191,14 +190,8 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
 
        strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
 
-       memset(&child, 0, sizeof(child));
-       child.argv = args;
-       args[0] = "sh";
-       args[1] = "-c";
        args[2] = cmd.buf;
-       args[3] = NULL;
-
-       status = run_command(&child);
+       status = run_command_v_opt(args, 0);
        if (status < -ERR_RUN_COMMAND_FORK)
                ; /* failure in run-command */
        else
@@ -238,7 +231,7 @@ static int read_merge_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(var, "merge.default")) {
                if (value)
-                       default_ll_merge = strdup(value);
+                       default_ll_merge = xstrdup(value);
                return 0;
        }
 
@@ -272,7 +265,7 @@ static int read_merge_config(const char *var, const char *value, void *cb)
        if (!strcmp("name", ep)) {
                if (!value)
                        return error("%s: lacks value", var);
-               fn->description = strdup(value);
+               fn->description = xstrdup(value);
                return 0;
        }
 
@@ -295,14 +288,14 @@ static int read_merge_config(const char *var, const char *value, void *cb)
                 * file named by %A, and signal that it has done with zero exit
                 * status.
                 */
-               fn->cmdline = strdup(value);
+               fn->cmdline = xstrdup(value);
                return 0;
        }
 
        if (!strcmp("recursive", ep)) {
                if (!value)
                        return error("%s: lacks value", var);
-               fn->recursive = strdup(value);
+               fn->recursive = xstrdup(value);
                return 0;
        }
 
index 5bd29e6994c92268ec576671bb8564b57d1a5c9d..59d63eb67e57ab55bdca6493fa8b28b0c870ff21 100644 (file)
@@ -25,6 +25,7 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        struct object *obj = parse_object(sha1);
        if (!obj)
                return 0;
+       refname = prettify_refname(refname);
        add_name_decoration("", refname, obj);
        while (obj->type == OBJ_TAG) {
                obj = ((struct tag *)obj)->tagged;
index aa9cf23a39ae271a53d1a0c05ac99be0e832b46a..19ddd03e0b2a57d03d85b1e5ca73b0bd4a0d6981 100644 (file)
@@ -3,45 +3,20 @@
 #include "exec_cmd.h"
 
 static const char *pgm;
-static const char *arguments[9];
 static int one_shot, quiet;
 static int err;
 
-static void run_program(void)
-{
-       struct child_process child;
-       memset(&child, 0, sizeof(child));
-       child.argv = arguments;
-       if (run_command(&child)) {
-               if (one_shot) {
-                       err++;
-               } else {
-                       if (!quiet)
-                               die("merge program failed");
-                       exit(1);
-               }
-       }
-}
-
 static int merge_entry(int pos, const char *path)
 {
        int found;
+       const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
+       char hexbuf[4][60];
+       char ownbuf[4][60];
 
        if (pos >= active_nr)
                die("git merge-index: %s not in the cache", path);
-       arguments[0] = pgm;
-       arguments[1] = "";
-       arguments[2] = "";
-       arguments[3] = "";
-       arguments[4] = path;
-       arguments[5] = "";
-       arguments[6] = "";
-       arguments[7] = "";
-       arguments[8] = NULL;
        found = 0;
        do {
-               static char hexbuf[4][60];
-               static char ownbuf[4][60];
                struct cache_entry *ce = active_cache[pos];
                int stage = ce_stage(ce);
 
@@ -55,7 +30,16 @@ static int merge_entry(int pos, const char *path)
        } while (++pos < active_nr);
        if (!found)
                die("git merge-index: %s not in the cache", path);
-       run_program();
+
+       if (run_command_v_opt(arguments, 0)) {
+               if (one_shot)
+                       err++;
+               else {
+                       if (!quiet)
+                               die("merge program failed");
+                       exit(1);
+               }
+       }
        return found;
 }
 
diff --git a/mktree.c b/mktree.c
deleted file mode 100644 (file)
index 137a095..0000000
--- a/mktree.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * GIT - the stupid content tracker
- *
- * Copyright (c) Junio C Hamano, 2006
- */
-#include "cache.h"
-#include "quote.h"
-#include "tree.h"
-#include "exec_cmd.h"
-
-static struct treeent {
-       unsigned mode;
-       unsigned char sha1[20];
-       int len;
-       char name[FLEX_ARRAY];
-} **entries;
-static int alloc, used;
-
-static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
-{
-       struct treeent *ent;
-       int len = strlen(path);
-       if (strchr(path, '/'))
-               die("path %s contains slash", path);
-
-       if (alloc <= used) {
-               alloc = alloc_nr(used);
-               entries = xrealloc(entries, sizeof(*entries) * alloc);
-       }
-       ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1);
-       ent->mode = mode;
-       ent->len = len;
-       hashcpy(ent->sha1, sha1);
-       memcpy(ent->name, path, len+1);
-}
-
-static int ent_compare(const void *a_, const void *b_)
-{
-       struct treeent *a = *(struct treeent **)a_;
-       struct treeent *b = *(struct treeent **)b_;
-       return base_name_compare(a->name, a->len, a->mode,
-                                b->name, b->len, b->mode);
-}
-
-static void write_tree(unsigned char *sha1)
-{
-       struct strbuf buf;
-       size_t size;
-       int i;
-
-       qsort(entries, used, sizeof(*entries), ent_compare);
-       for (size = i = 0; i < used; i++)
-               size += 32 + entries[i]->len;
-
-       strbuf_init(&buf, size);
-       for (i = 0; i < used; i++) {
-               struct treeent *ent = entries[i];
-               strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0');
-               strbuf_add(&buf, ent->sha1, 20);
-       }
-
-       write_sha1_file(buf.buf, buf.len, tree_type, sha1);
-}
-
-static const char mktree_usage[] = "git mktree [-z]";
-
-int main(int ac, char **av)
-{
-       struct strbuf sb = STRBUF_INIT;
-       struct strbuf p_uq = STRBUF_INIT;
-       unsigned char sha1[20];
-       int line_termination = '\n';
-
-       git_extract_argv0_path(av[0]);
-
-       setup_git_directory();
-
-       while ((1 < ac) && av[1][0] == '-') {
-               char *arg = av[1];
-               if (!strcmp("-z", arg))
-                       line_termination = 0;
-               else
-                       usage(mktree_usage);
-               ac--;
-               av++;
-       }
-
-       while (strbuf_getline(&sb, stdin, line_termination) != EOF) {
-               char *ptr, *ntr;
-               unsigned mode;
-               enum object_type type;
-               char *path;
-
-               ptr = sb.buf;
-               /* Input is non-recursive ls-tree output format
-                * mode SP type SP sha1 TAB name
-                */
-               mode = strtoul(ptr, &ntr, 8);
-               if (ptr == ntr || !ntr || *ntr != ' ')
-                       die("input format error: %s", sb.buf);
-               ptr = ntr + 1; /* type */
-               ntr = strchr(ptr, ' ');
-               if (!ntr || sb.buf + sb.len <= ntr + 40 ||
-                   ntr[41] != '\t' ||
-                   get_sha1_hex(ntr + 1, sha1))
-                       die("input format error: %s", sb.buf);
-               type = sha1_object_info(sha1, NULL);
-               if (type < 0)
-                       die("object %s unavailable", sha1_to_hex(sha1));
-               *ntr++ = 0; /* now at the beginning of SHA1 */
-               if (type != type_from_string(ptr))
-                       die("object type %s mismatch (%s)", ptr, typename(type));
-
-               path = ntr + 41;  /* at the beginning of name */
-               if (line_termination && path[0] == '"') {
-                       strbuf_reset(&p_uq);
-                       if (unquote_c_style(&p_uq, path, NULL)) {
-                               die("invalid quoting");
-                       }
-                       path = p_uq.buf;
-               }
-
-               append_to_tree(mode, sha1, path);
-       }
-       strbuf_release(&p_uq);
-       strbuf_release(&sb);
-
-       write_tree(sha1);
-       puts(sha1_to_hex(sha1));
-       exit(0);
-}
index e1feef9c3329e0370e7caff612b4f6c8684cbaef..a6ef439192c1083e367f0a86cb10d93564fc9481 100644 (file)
--- a/object.c
+++ b/object.c
@@ -52,7 +52,7 @@ static unsigned int hash_obj(struct object *obj, unsigned int n)
 
 static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size)
 {
-       int j = hash_obj(obj, size);
+       unsigned int j = hash_obj(obj, size);
 
        while (hash[j]) {
                j++;
@@ -62,16 +62,16 @@ static void insert_obj_hash(struct object *obj, struct object **hash, unsigned i
        hash[j] = obj;
 }
 
-static int hashtable_index(const unsigned char *sha1)
+static unsigned int hashtable_index(const unsigned char *sha1)
 {
        unsigned int i;
        memcpy(&i, sha1, sizeof(unsigned int));
-       return (int)(i % obj_hash_size);
+       return i % obj_hash_size;
 }
 
 struct object *lookup_object(const unsigned char *sha1)
 {
-       int i;
+       unsigned int i;
        struct object *obj;
 
        if (!obj_hash)
index 90c33b1b84ef793bf017927026863de41e31fa2c..166ca703c10face0d4961da6ceee7a149ebcfac4 100644 (file)
@@ -49,7 +49,7 @@ static int verify_packfile(struct packed_git *p,
        const unsigned char *index_base = p->index_data;
        git_SHA_CTX ctx;
        unsigned char sha1[20], *pack_sig;
-       off_t offset = 0, pack_sig_ofs = p->pack_size - 20;
+       off_t offset = 0, pack_sig_ofs = 0;
        uint32_t nr_objects, i;
        int err = 0;
        struct idx_entry *entries;
@@ -61,14 +61,16 @@ static int verify_packfile(struct packed_git *p,
         */
 
        git_SHA1_Init(&ctx);
-       while (offset < pack_sig_ofs) {
+       do {
                unsigned int remaining;
                unsigned char *in = use_pack(p, w_curs, offset, &remaining);
                offset += remaining;
+               if (!pack_sig_ofs)
+                       pack_sig_ofs = p->pack_size - 20;
                if (offset > pack_sig_ofs)
                        remaining -= (unsigned int)(offset - pack_sig_ofs);
                git_SHA1_Update(&ctx, in, remaining);
-       }
+       } while (offset < pack_sig_ofs);
        git_SHA1_Final(sha1, &ctx);
        pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL);
        if (hashcmp(sha1, pack_sig))
index cf71bcffd2e1e9aeacb44df488b0825f09d54255..34282adde5a2fd8ebb1413aee6742633b9656db7 100644 (file)
@@ -31,11 +31,20 @@ static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
        return 0;
 }
 
+static void fix_filename(const char *prefix, const char **file)
+{
+       if (!file || !*file || !prefix || is_absolute_path(*file)
+           || !strcmp("-", *file))
+               return;
+       *file = xstrdup(prefix_filename(prefix, strlen(prefix), *file));
+}
+
 static int get_value(struct parse_opt_ctx_t *p,
                     const struct option *opt, int flags)
 {
        const char *s, *arg;
        const int unset = flags & OPT_UNSET;
+       int err;
 
        if (unset && p->opt)
                return opterror(opt, "takes no value", flags);
@@ -50,6 +59,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                        /* FALLTHROUGH */
                case OPTION_BOOLEAN:
                case OPTION_BIT:
+               case OPTION_NEGBIT:
                case OPTION_SET_INT:
                case OPTION_SET_PTR:
                        return opterror(opt, "takes no value", flags);
@@ -66,6 +76,13 @@ static int get_value(struct parse_opt_ctx_t *p,
                        *(int *)opt->value |= opt->defval;
                return 0;
 
+       case OPTION_NEGBIT:
+               if (unset)
+                       *(int *)opt->value |= opt->defval;
+               else
+                       *(int *)opt->value &= ~opt->defval;
+               return 0;
+
        case OPTION_BOOLEAN:
                *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
                return 0;
@@ -87,6 +104,19 @@ static int get_value(struct parse_opt_ctx_t *p,
                        return get_arg(p, opt, flags, (const char **)opt->value);
                return 0;
 
+       case OPTION_FILENAME:
+               err = 0;
+               if (unset)
+                       *(const char **)opt->value = NULL;
+               else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+                       *(const char **)opt->value = (const char *)opt->defval;
+               else
+                       err = get_arg(p, opt, flags, (const char **)opt->value);
+
+               if (!err)
+                       fix_filename(p->prefix, (const char **)opt->value);
+               return err;
+
        case OPTION_CALLBACK:
                if (unset)
                        return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
@@ -121,11 +151,33 @@ static int get_value(struct parse_opt_ctx_t *p,
 
 static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
 {
+       const struct option *numopt = NULL;
+
        for (; options->type != OPTION_END; options++) {
                if (options->short_name == *p->opt) {
                        p->opt = p->opt[1] ? p->opt + 1 : NULL;
                        return get_value(p, options, OPT_SHORT);
                }
+
+               /*
+                * Handle the numerical option later, explicit one-digit
+                * options take precedence over it.
+                */
+               if (options->type == OPTION_NUMBER)
+                       numopt = options;
+       }
+       if (numopt && isdigit(*p->opt)) {
+               size_t len = 1;
+               char *arg;
+               int rc;
+
+               while (isdigit(p->opt[len]))
+                       len++;
+               arg = xmemdupz(p->opt, len);
+               p->opt = p->opt[len] ? p->opt + len : NULL;
+               rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0;
+               free(arg);
+               return rc;
        }
        return -2;
 }
@@ -215,6 +267,25 @@ is_abbreviated:
        return -2;
 }
 
+static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
+                           const struct option *options)
+{
+       for (; options->type != OPTION_END; options++) {
+               if (!(options->flags & PARSE_OPT_NODASH))
+                       continue;
+               if ((options->flags & PARSE_OPT_OPTARG) ||
+                   !(options->flags & PARSE_OPT_NOARG))
+                       die("BUG: dashless options don't support arguments");
+               if (!(options->flags & PARSE_OPT_NONEG))
+                       die("BUG: dashless options don't support negation");
+               if (options->long_name)
+                       die("BUG: dashless options can't be long");
+               if (options->short_name == arg[0] && arg[1] == '\0')
+                       return get_value(p, options, OPT_SHORT);
+       }
+       return -2;
+}
+
 static void check_typos(const char *arg, const struct option *options)
 {
        if (strlen(arg) < 3)
@@ -235,13 +306,37 @@ static void check_typos(const char *arg, const struct option *options)
        }
 }
 
+static void parse_options_check(const struct option *opts)
+{
+       int err = 0;
+
+       for (; opts->type != OPTION_END; opts++) {
+               if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
+                   (opts->flags & PARSE_OPT_OPTARG)) {
+                       if (opts->long_name) {
+                               error("`--%s` uses incompatible flags "
+                                     "LASTARG_DEFAULT and OPTARG", opts->long_name);
+                       } else {
+                               error("`-%c` uses incompatible flags "
+                                     "LASTARG_DEFAULT and OPTARG", opts->short_name);
+                       }
+                       err |= 1;
+               }
+       }
+
+       if (err)
+               exit(129);
+}
+
 void parse_options_start(struct parse_opt_ctx_t *ctx,
-                        int argc, const char **argv, int flags)
+                        int argc, const char **argv, const char *prefix,
+                        int flags)
 {
        memset(ctx, 0, sizeof(*ctx));
        ctx->argc = argc - 1;
        ctx->argv = argv + 1;
        ctx->out  = argv;
+       ctx->prefix = prefix;
        ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
        ctx->flags = flags;
        if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
@@ -258,6 +353,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 {
        int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
 
+       parse_options_check(options);
+
        /* we must reset ->opt, unknown short option leave it dangling */
        ctx->opt = NULL;
 
@@ -265,6 +362,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                const char *arg = ctx->argv[0];
 
                if (*arg != '-' || !arg[1]) {
+                       if (parse_nodash_opt(ctx, arg, options) == 0)
+                               continue;
                        if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
                                break;
                        ctx->out[ctx->cpidx++] = ctx->argv[0];
@@ -338,12 +437,13 @@ int parse_options_end(struct parse_opt_ctx_t *ctx)
        return ctx->cpidx + ctx->argc;
 }
 
-int parse_options(int argc, const char **argv, const struct option *options,
-                 const char * const usagestr[], int flags)
+int parse_options(int argc, const char **argv, const char *prefix,
+                 const struct option *options, const char * const usagestr[],
+                 int flags)
 {
        struct parse_opt_ctx_t ctx;
 
-       parse_options_start(&ctx, argc, argv, flags);
+       parse_options_start(&ctx, argc, argv, prefix, flags);
        switch (parse_options_step(&ctx, options, usagestr)) {
        case PARSE_OPT_HELP:
                exit(129);
@@ -361,6 +461,20 @@ int parse_options(int argc, const char **argv, const struct option *options,
        return parse_options_end(&ctx);
 }
 
+static int usage_argh(const struct option *opts)
+{
+       const char *s;
+       int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) || !opts->argh;
+       if (opts->flags & PARSE_OPT_OPTARG)
+               if (opts->long_name)
+                       s = literal ? "[=%s]" : "[=<%s>]";
+               else
+                       s = literal ? "[%s]" : "[<%s>]";
+       else
+               s = literal ? " %s" : " <%s>";
+       return fprintf(stderr, s, opts->argh ? opts->argh : "...");
+}
+
 #define USAGE_OPTS_WIDTH 24
 #define USAGE_GAP         2
 
@@ -397,51 +511,21 @@ int usage_with_options_internal(const char * const *usagestr,
                        continue;
 
                pos = fprintf(stderr, "    ");
-               if (opts->short_name)
-                       pos += fprintf(stderr, "-%c", opts->short_name);
+               if (opts->short_name) {
+                       if (opts->flags & PARSE_OPT_NODASH)
+                               pos += fprintf(stderr, "%c", opts->short_name);
+                       else
+                               pos += fprintf(stderr, "-%c", opts->short_name);
+               }
                if (opts->long_name && opts->short_name)
                        pos += fprintf(stderr, ", ");
                if (opts->long_name)
                        pos += fprintf(stderr, "--%s", opts->long_name);
+               if (opts->type == OPTION_NUMBER)
+                       pos += fprintf(stderr, "-NUM");
 
-               switch (opts->type) {
-               case OPTION_ARGUMENT:
-                       break;
-               case OPTION_INTEGER:
-                       if (opts->flags & PARSE_OPT_OPTARG)
-                               if (opts->long_name)
-                                       pos += fprintf(stderr, "[=<n>]");
-                               else
-                                       pos += fprintf(stderr, "[<n>]");
-                       else
-                               pos += fprintf(stderr, " <n>");
-                       break;
-               case OPTION_CALLBACK:
-                       if (opts->flags & PARSE_OPT_NOARG)
-                               break;
-                       /* FALLTHROUGH */
-               case OPTION_STRING:
-                       if (opts->argh) {
-                               if (opts->flags & PARSE_OPT_OPTARG)
-                                       if (opts->long_name)
-                                               pos += fprintf(stderr, "[=<%s>]", opts->argh);
-                                       else
-                                               pos += fprintf(stderr, "[<%s>]", opts->argh);
-                               else
-                                       pos += fprintf(stderr, " <%s>", opts->argh);
-                       } else {
-                               if (opts->flags & PARSE_OPT_OPTARG)
-                                       if (opts->long_name)
-                                               pos += fprintf(stderr, "[=...]");
-                                       else
-                                               pos += fprintf(stderr, "[...]");
-                               else
-                                       pos += fprintf(stderr, " ...");
-                       }
-                       break;
-               default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */
-                       break;
-               }
+               if (!(opts->flags & PARSE_OPT_NOARG))
+                       pos += usage_argh(opts);
 
                if (pos <= USAGE_OPTS_WIDTH)
                        pad = USAGE_OPTS_WIDTH - pos;
@@ -536,15 +620,3 @@ int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
        commit_list_insert(commit, opt->value);
        return 0;
 }
-
-/*
- * This should really be OPTION_FILENAME type as a part of
- * parse_options that take prefix to do this while parsing.
- */
-extern const char *parse_options_fix_filename(const char *prefix, const char *file)
-{
-       if (!file || !prefix || is_absolute_path(file) || !strcmp("-", file))
-               return file;
-       return prefix_filename(prefix, strlen(prefix), file);
-}
-
index b54eec128bcda1a8b4c4a2c33f2ce799c506ac23..aba30671dccd9e90686363a766df3eb25d4a8e20 100644 (file)
@@ -6,8 +6,10 @@ enum parse_opt_type {
        OPTION_END,
        OPTION_ARGUMENT,
        OPTION_GROUP,
+       OPTION_NUMBER,
        /* options with no arguments */
        OPTION_BIT,
+       OPTION_NEGBIT,
        OPTION_BOOLEAN, /* _INCR would have been a better name */
        OPTION_SET_INT,
        OPTION_SET_PTR,
@@ -15,6 +17,7 @@ enum parse_opt_type {
        OPTION_STRING,
        OPTION_INTEGER,
        OPTION_CALLBACK,
+       OPTION_FILENAME
 };
 
 enum parse_opt_flags {
@@ -31,6 +34,8 @@ enum parse_opt_option_flags {
        PARSE_OPT_NONEG   = 4,
        PARSE_OPT_HIDDEN  = 8,
        PARSE_OPT_LASTARG_DEFAULT = 16,
+       PARSE_OPT_NODASH = 32,
+       PARSE_OPT_LITERAL_ARGHELP = 64,
 };
 
 struct option;
@@ -62,10 +67,19 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
  * `flags`::
  *   mask of parse_opt_option_flags.
  *   PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs)
- *   PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
+ *   PARSE_OPT_NOARG: says that this option takes no argument
  *   PARSE_OPT_NONEG: says that this option cannot be negated
- *   PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in
- *                    the long one.
+ *   PARSE_OPT_HIDDEN: this option is skipped in the default usage, and
+ *                     shown only in the full usage.
+ *   PARSE_OPT_LASTARG_DEFAULT: says that this option will take the default
+ *                             value if no argument is given when the option
+ *                             is last on the command line. If the option is
+ *                             not last it will require an argument.
+ *                             Should not be used with PARSE_OPT_OPTARG.
+ *   PARSE_OPT_NODASH: this option doesn't start with a dash.
+ *   PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets
+ *                             (i.e. '<argh>') in the help message.
+ *                             Useful for options with multiple parameters.
  *
  * `callback`::
  *   pointer to the callback to use for OPTION_CALLBACK.
@@ -90,25 +104,37 @@ struct option {
 };
 
 #define OPT_END()                   { OPTION_END }
-#define OPT_ARGUMENT(l, h)          { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) }
+#define OPT_ARGUMENT(l, h)          { OPTION_ARGUMENT, 0, (l), NULL, NULL, \
+                                     (h), PARSE_OPT_NOARG}
 #define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
-#define OPT_BIT(s, l, v, h, b)      { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) }
-#define OPT_BOOLEAN(s, l, v, h)     { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
-#define OPT_SET_INT(s, l, v, h, i)  { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) }
-#define OPT_SET_PTR(s, l, v, h, p)  { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) }
-#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), NULL, (h) }
+#define OPT_BIT(s, l, v, h, b)      { OPTION_BIT, (s), (l), (v), NULL, (h), \
+                                     PARSE_OPT_NOARG, NULL, (b) }
+#define OPT_NEGBIT(s, l, v, h, b)   { OPTION_NEGBIT, (s), (l), (v), NULL, \
+                                     (h), PARSE_OPT_NOARG, NULL, (b) }
+#define OPT_BOOLEAN(s, l, v, h)     { OPTION_BOOLEAN, (s), (l), (v), NULL, \
+                                     (h), PARSE_OPT_NOARG }
+#define OPT_SET_INT(s, l, v, h, i)  { OPTION_SET_INT, (s), (l), (v), NULL, \
+                                     (h), PARSE_OPT_NOARG, NULL, (i) }
+#define OPT_SET_PTR(s, l, v, h, p)  { OPTION_SET_PTR, (s), (l), (v), NULL, \
+                                     (h), PARSE_OPT_NOARG, NULL, (p) }
+#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), "n", (h) }
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
 #define OPT_DATE(s, l, v, h) \
        { OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \
          parse_opt_approxidate_cb }
 #define OPT_CALLBACK(s, l, v, a, h, f) \
        { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
+#define OPT_NUMBER_CALLBACK(v, h, f) \
+       { OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
+         PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
+#define OPT_FILENAME(s, l, v, h)    { OPTION_FILENAME, (s), (l), (v), \
+                                      "FILE", (h) }
 
 /* parse_options() will filter out the processed options and leave the
  * non-option arguments in argv[].
  * Returns the number of arguments left in argv[].
  */
-extern int parse_options(int argc, const char **argv,
+extern int parse_options(int argc, const char **argv, const char *prefix,
                          const struct option *options,
                          const char * const usagestr[], int flags);
 
@@ -134,13 +160,15 @@ struct parse_opt_ctx_t {
        int argc, cpidx;
        const char *opt;
        int flags;
+       const char *prefix;
 };
 
 extern int parse_options_usage(const char * const *usagestr,
                               const struct option *opts);
 
 extern void parse_options_start(struct parse_opt_ctx_t *ctx,
-                               int argc, const char **argv, int flags);
+                               int argc, const char **argv, const char *prefix,
+                               int flags);
 
 extern int parse_options_step(struct parse_opt_ctx_t *ctx,
                              const struct option *options,
@@ -168,6 +196,4 @@ extern int parse_opt_with_commit(const struct option *, const char *, int);
          "use <n> digits to display SHA-1s", \
          PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
 
-extern const char *parse_options_fix_filename(const char *prefix, const char *file);
-
 #endif
index ed9db81fa82c812c9ffa07f5a40540dbb15da0d3..ef748ce96d246bb17c6e64b51b882d535d7f7774 100644 (file)
@@ -44,7 +44,7 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
                        if (cmd & 0x01) cp_off = *data++;
                        if (cmd & 0x02) cp_off |= (*data++ << 8);
                        if (cmd & 0x04) cp_off |= (*data++ << 16);
-                       if (cmd & 0x08) cp_off |= (*data++ << 24);
+                       if (cmd & 0x08) cp_off |= ((unsigned) *data++ << 24);
                        if (cmd & 0x10) cp_size = *data++;
                        if (cmd & 0x20) cp_size |= (*data++ << 8);
                        if (cmd & 0x40) cp_size |= (*data++ << 16);
diff --git a/path.c b/path.c
index 8a0a6741fd664f98f2883348c0a755d60616035b..047fdb0a1fe8151f5f275ca5333365df786a8abd 100644 (file)
--- a/path.c
+++ b/path.c
@@ -139,6 +139,22 @@ int git_mkstemp(char *path, size_t len, const char *template)
        return mkstemp(path);
 }
 
+/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */
+int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
+{
+       const char *tmp;
+       size_t n;
+
+       tmp = getenv("TMPDIR");
+       if (!tmp)
+               tmp = "/tmp";
+       n = snprintf(path, len, "%s/%s", tmp, template);
+       if (len <= n) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       return mkstemps(path, suffix_len);
+}
 
 int validate_headref(const char *path)
 {
index 291ff5b53c1883ee8fce67fbb7e2b32393ef0800..e8df55d2f290210ba4cf7ae8c91639f2a34c834e 100644 (file)
@@ -185,7 +185,7 @@ sub repository {
 
                if ($dir) {
                        $dir =~ m#^/# or $dir = $opts{Directory} . '/' . $dir;
-                       $opts{Repository} = $dir;
+                       $opts{Repository} = abs_path($dir);
 
                        # If --git-dir went ok, this shouldn't die either.
                        my $prefix = $search->command_oneline('rev-parse', '--show-prefix');
@@ -1280,6 +1280,8 @@ sub _cmd_exec {
        my ($self, @args) = @_;
        if ($self) {
                $self->repo_path() and $ENV{'GIT_DIR'} = $self->repo_path();
+               $self->repo_path() and $self->wc_path()
+                       and $ENV{'GIT_WORK_TREE'} = $self->wc_path();
                $self->wc_path() and chdir($self->wc_path());
                $self->wc_subdir() and chdir($self->wc_subdir());
        }
index a0ef356558f4cdb148010f1b47dbd3fcc363d8ba..e5328dab5b2978d90d20f47ed799782e7b2fb63b 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -284,7 +284,7 @@ static char *replace_encoding_header(char *buf, const char *encoding)
 static char *logmsg_reencode(const struct commit *commit,
                             const char *output_encoding)
 {
-       static const char *utf8 = "utf-8";
+       static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
        char *out;
@@ -881,7 +881,7 @@ char *reencode_commit_message(const struct commit *commit, const char **encoding
                    ? git_log_output_encoding
                    : git_commit_encoding);
        if (!encoding)
-               encoding = "utf-8";
+               encoding = "UTF-8";
        if (encoding_p)
                *encoding_p = encoding;
        return logmsg_reencode(commit, encoding);
diff --git a/refs.c b/refs.c
index 90163bdc56868151e4fd45ff77aa3bd56db45573..24438c652fe4e09aaa1ba6dab283b8e59c24c1a7 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -682,12 +682,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
  * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
  * - it ends with a "/".
  * - it ends with ".lock"
+ * - it contains a "\" (backslash)
  */
 
 static inline int bad_ref_char(int ch)
 {
        if (((unsigned) ch) <= ' ' ||
-           ch == '~' || ch == '^' || ch == ':')
+           ch == '~' || ch == '^' || ch == ':' || ch == '\\')
                return 1;
        /* 2.13 Pattern Matching Notation */
        if (ch == '?' || ch == '[') /* Unsupported */
@@ -750,9 +751,8 @@ int check_ref_format(const char *ref)
        }
 }
 
-const char *prettify_ref(const struct ref *ref)
+const char *prettify_refname(const char *name)
 {
-       const char *name = ref->name;
        return name + (
                !prefixcmp(name, "refs/heads/") ? 11 :
                !prefixcmp(name, "refs/tags/") ? 10 :
diff --git a/refs.h b/refs.h
index 29d17a48e4a2923bc72337deb1ef64cf7b467381..c11f6a6d588d1bd737fa60241b9c67308f4c14cc 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -80,7 +80,7 @@ extern int for_each_reflog(each_ref_fn, void *);
 #define CHECK_REF_FORMAT_WILDCARD (-3)
 extern int check_ref_format(const char *target);
 
-extern const char *prettify_ref(const struct ref *ref);
+extern const char *prettify_refname(const char *refname);
 extern char *shorten_unambiguous_ref(const char *ref, int strict);
 
 /** rename ref, return 0 on success **/
index d66e2f3c93dc72a7112ce101278ae937cc914320..08a59644c03b9c61bfad8383e521ac813e0c4f05 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1085,12 +1085,20 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
                return NULL;
 }
 
+static struct ref **tail_ref(struct ref **head)
+{
+       struct ref **tail = head;
+       while (*tail)
+               tail = &((*tail)->next);
+       return tail;
+}
+
 /*
  * Note. This is used only by "push"; refspec matching rules for
  * push and fetch are subtly different, so do not try to reuse it
  * without thinking.
  */
-int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
+int match_refs(struct ref *src, struct ref **dst,
               int nr_refspec, const char **refspec, int flags)
 {
        struct refspec *rs;
@@ -1098,13 +1106,14 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
        int send_mirror = flags & MATCH_REFS_MIRROR;
        int errs;
        static const char *default_refspec[] = { ":", 0 };
+       struct ref **dst_tail = tail_ref(dst);
 
        if (!nr_refspec) {
                nr_refspec = 1;
                refspec = default_refspec;
        }
        rs = parse_push_refspec(nr_refspec, (const char **) refspec);
-       errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec);
+       errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
 
        /* pick the remainder */
        for ( ; src; src = src->next) {
@@ -1134,7 +1143,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                                                     dst_side, &dst_name))
                                die("Didn't think it matches any more");
                }
-               dst_peer = find_ref_by_name(dst, dst_name);
+               dst_peer = find_ref_by_name(*dst, dst_name);
                if (dst_peer) {
                        if (dst_peer->peer_ref)
                                /* We're already sending something to this ref. */
@@ -1150,7 +1159,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                                goto free_name;
 
                        /* Create a new one and link it */
-                       dst_peer = make_linked_ref(dst_name, dst_tail);
+                       dst_peer = make_linked_ref(dst_name, &dst_tail);
                        hashcpy(dst_peer->new_sha1, src->new_sha1);
                }
                dst_peer->peer_ref = copy_ref(src);
@@ -1399,13 +1408,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
        base = branch->merge[0]->dst;
        if (!resolve_ref(base, sha1, 1, NULL))
                return 0;
-       theirs = lookup_commit(sha1);
+       theirs = lookup_commit_reference(sha1);
        if (!theirs)
                return 0;
 
        if (!resolve_ref(branch->refname, sha1, 1, NULL))
                return 0;
-       ours = lookup_commit(sha1);
+       ours = lookup_commit_reference(sha1);
        if (!ours)
                return 0;
 
index 99706a89bc6011c01fcd661d8bad4b26f59b0ca7..257a55538f3ff2d6330b84436a40d6ef972025a0 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -85,7 +85,7 @@ void ref_remove_duplicates(struct ref *ref_map);
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 
-int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
+int match_refs(struct ref *src, struct ref **dst,
               int nr_refspec, const char **refspec, int all);
 
 /*
index 18b7ebbbd599417462b5c4275c2581b91ebeca6a..bf584483675ef55b2ed9b8d8586b92d5432759a5 100644 (file)
@@ -256,10 +256,12 @@ static int everybody_uninteresting(struct commit_list *orig)
 
 /*
  * The goal is to get REV_TREE_NEW as the result only if the
- * diff consists of all '+' (and no other changes), and
- * REV_TREE_DIFFERENT otherwise (of course if the trees are
- * the same we want REV_TREE_SAME).  That means that once we
- * get to REV_TREE_DIFFERENT, we do not have to look any further.
+ * diff consists of all '+' (and no other changes), REV_TREE_OLD
+ * if the whole diff is removal of old data, and otherwise
+ * REV_TREE_DIFFERENT (of course if the trees are the same we
+ * want REV_TREE_SAME).
+ * That means that once we get to REV_TREE_DIFFERENT, we do not
+ * have to look any further.
  */
 static int tree_difference = REV_TREE_SAME;
 
@@ -268,22 +270,9 @@ static void file_add_remove(struct diff_options *options,
                    const unsigned char *sha1,
                    const char *fullpath)
 {
-       int diff = REV_TREE_DIFFERENT;
+       int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
 
-       /*
-        * Is it an add of a new file? It means that the old tree
-        * didn't have it at all, so we will turn "REV_TREE_SAME" ->
-        * "REV_TREE_NEW", but leave any "REV_TREE_DIFFERENT" alone
-        * (and if it already was "REV_TREE_NEW", we'll keep it
-        * "REV_TREE_NEW" of course).
-        */
-       if (addremove == '+') {
-               diff = tree_difference;
-               if (diff != REV_TREE_SAME)
-                       return;
-               diff = REV_TREE_NEW;
-       }
-       tree_difference = diff;
+       tree_difference |= diff;
        if (tree_difference == REV_TREE_DIFFERENT)
                DIFF_OPT_SET(options, HAS_CHANGES);
 }
@@ -305,6 +294,8 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct
 
        if (!t1)
                return REV_TREE_NEW;
+       if (!t2)
+               return REV_TREE_OLD;
 
        if (revs->simplify_by_decoration) {
                /*
@@ -323,8 +314,7 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct
                if (!revs->prune_data)
                        return REV_TREE_SAME;
        }
-       if (!t2)
-               return REV_TREE_DIFFERENT;
+
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
@@ -429,6 +419,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                                p->parents = NULL;
                        }
                /* fallthrough */
+               case REV_TREE_OLD:
                case REV_TREE_DIFFERENT:
                        tree_changed = 1;
                        pp = &parent->next;
index be39e7d38643817cced4e07a89b6db5201f2b747..227164cf7096d3b0642714b18ee345738684da79 100644 (file)
@@ -118,8 +118,9 @@ struct rev_info {
 };
 
 #define REV_TREE_SAME          0
-#define REV_TREE_NEW           1
-#define REV_TREE_DIFFERENT     2
+#define REV_TREE_NEW           1       /* Only new files */
+#define REV_TREE_OLD           2       /* Only files removed */
+#define REV_TREE_DIFFERENT     3       /* Mixed changes */
 
 /* revision.c */
 void read_revisions_from_stdin(struct rev_info *revs);
index e73cd4fc0ba2daac14f604f1973d1b0658212b26..8f5fe62d545ace21c338cd554c76bac5d5acb431 100644 (file)
@@ -1162,8 +1162,7 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
                unsigned long len, enum object_type *type, unsigned long *sizep)
 {
        unsigned shift;
-       unsigned char c;
-       unsigned long size;
+       unsigned long size, c;
        unsigned long used = 0;
 
        c = buf[used++];
index 1d6b35b858020300f502e2a9341b82d4fa8a61fd..8dcd63261fff56e54c233224c6014b146d9bbeb3 100644 (file)
@@ -196,8 +196,9 @@ void invalidate_lstat_cache(const char *name, int len)
                        cache.path[previous_slash] = '\0';
                        cache.len = previous_slash;
                        cache.flags = FL_DIR;
-               } else
+               } else {
                        reset_lstat_cache();
+               }
        }
 }
 
@@ -263,7 +264,6 @@ static void do_remove_scheduled_dirs(int new_len)
                         removal.path[removal.len] != '/');
        }
        removal.len = new_len;
-       return;
 }
 
 void schedule_dir_for_removal(const char *name, int len)
@@ -296,11 +296,9 @@ void schedule_dir_for_removal(const char *name, int len)
                       last_slash - match_len);
                removal.len = last_slash;
        }
-       return;
 }
 
 void remove_scheduled_dirs(void)
 {
        do_remove_scheduled_dirs(0);
-       return;
 }
index 773d47cf3cb704d7976185738a2b9840c159a33c..56549623430ca6bea2de35bbae7d2d211720da3b 100644 (file)
@@ -26,6 +26,8 @@ fi
 
 svnrepo=$PWD/svnrepo
 export svnrepo
+svnconf=$PWD/svnconf
+export svnconf
 
 perl -w -e "
 use SVN::Core;
@@ -54,6 +56,19 @@ poke() {
        test-chmtime +1 "$1"
 }
 
+# We need this, because we should pass empty configuration directory to
+# the 'svn commit' to avoid automated property changes and other stuff
+# that could be set from user's configuration files in ~/.subversion.
+svn_cmd () {
+       [ -d "$svnconf" ] || mkdir "$svnconf"
+       orig_svncmd="$1"; shift
+       if [ -z "$orig_svncmd" ]; then
+               svn
+               return
+       fi
+       svn "$orig_svncmd" --config-dir "$svnconf" "$@"
+}
+
 for d in \
        "$SVN_HTTPD_PATH" \
        /usr/sbin/apache2 \
index cde659d14ac599e78bb4cd12a2ad88eab179549e..6765b08065e8959dcec38940188818b952d2fcd0 100644 (file)
@@ -93,14 +93,16 @@ prepare_httpd() {
 start_httpd() {
        prepare_httpd >&3 2>&4
 
-       trap 'stop_httpd; die' EXIT
+       trap 'code=$?; stop_httpd; (exit $code); die' EXIT
 
        "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
                -f "$TEST_PATH/apache.conf" $HTTPD_PARA \
                -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
                >&3 2>&4
-       if ! test $? = 0; then
+       if test $? -ne 0
+       then
                say "skipping test, web server setup failed"
+               trap 'die' EXIT
                test_done
        fi
 }
index e38241c80a6625c9b5b89340b9d0c5a2667bf345..bbc821ef97de76ef532ec1d1d704da15e8973500 100755 (executable)
@@ -12,12 +12,14 @@ usage: test-parse-options <options>
 
     -b, --boolean         get a boolean
     -4, --or4             bitwise-or boolean with ...0100
+    --neg-or4             same as --no-or4
 
     -i, --integer <n>     get a integer
     -j <n>                get a integer, too
     --set23               set integer to 23
     -t <time>             get timestamp of <time>
     -L, --length <str>    get length of <str>
+    -F, --file <FILE>     set file to <FILE>
 
 String options
     -s, --string <string>
@@ -29,6 +31,8 @@ String options
 
 Magic arguments
     --quux                means --quux
+    -NUM                  set integer to NUM
+    +                     same as -b
 
 Standard options
     --abbrev[=<n>]        use <n> digits to display SHA-1s
@@ -53,10 +57,12 @@ abbrev: 7
 verbose: 2
 quiet: no
 dry run: yes
+file: prefix/my.file
 EOF
 
 test_expect_success 'short options' '
-       test-parse-options -s123 -b -i 1729 -b -vv -n > output 2> output.err &&
+       test-parse-options -s123 -b -i 1729 -b -vv -n -F my.file \
+       > output 2> output.err &&
        test_cmp expect output &&
        test ! -s output.err
 '
@@ -70,11 +76,12 @@ abbrev: 10
 verbose: 2
 quiet: no
 dry run: no
+file: prefix/fi.le
 EOF
 
 test_expect_success 'long options' '
        test-parse-options --boolean --integer 1729 --boolean --string2=321 \
-               --verbose --verbose --no-dry-run --abbrev=10 \
+               --verbose --verbose --no-dry-run --abbrev=10 --file fi.le\
                > output 2> output.err &&
        test ! -s output.err &&
        test_cmp expect output
@@ -84,6 +91,8 @@ test_expect_success 'missing required value' '
        test-parse-options -s;
        test $? = 129 &&
        test-parse-options --string;
+       test $? = 129 &&
+       test-parse-options --file;
        test $? = 129
 '
 
@@ -96,6 +105,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 arg 00: a1
 arg 01: b1
 arg 02: --boolean
@@ -117,6 +127,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 EOF
 
 test_expect_success 'unambiguously abbreviated option' '
@@ -145,6 +156,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 EOF
 
 test_expect_success 'non ambiguous option (after two options it abbreviates)' '
@@ -172,6 +184,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 arg 00: --quux
 EOF
 
@@ -190,6 +203,7 @@ abbrev: 7
 verbose: 0
 quiet: yes
 dry run: no
+file: (not set)
 arg 00: foo
 EOF
 
@@ -210,6 +224,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 EOF
 
 test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
@@ -237,6 +252,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 EOF
 
 test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
@@ -245,7 +261,58 @@ test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
        test_cmp expect output
 '
 
-# --or4
-# --no-or4
+test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' '
+       test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err &&
+       test ! -s output.err &&
+       test_cmp expect output
+'
+
+cat > expect <<EOF
+boolean: 6
+integer: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'OPT_BIT() works' '
+       test-parse-options -bb --or4 > output 2> output.err &&
+       test ! -s output.err &&
+       test_cmp expect output
+'
+
+test_expect_success 'OPT_NEGBIT() works' '
+       test-parse-options -bb --no-neg-or4 > output 2> output.err &&
+       test ! -s output.err &&
+       test_cmp expect output
+'
+
+test_expect_success 'OPT_BOOLEAN() with PARSE_OPT_NODASH works' '
+       test-parse-options + + + + + + > output 2> output.err &&
+       test ! -s output.err &&
+       test_cmp expect output
+'
+
+cat > expect <<EOF
+boolean: 0
+integer: 12345
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'OPT_NUMBER_CALLBACK() works' '
+       test-parse-options -12345 > output 2> output.err &&
+       test ! -s output.err &&
+       test_cmp expect output
+'
 
 test_done
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
new file mode 100755 (executable)
index 0000000..9956e3a
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+test_description='git mktree'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       for d in a a. a0
+       do
+               mkdir "$d" && echo "$d/one" >"$d/one" &&
+               git add "$d"
+       done &&
+       echo zero >one &&
+       git update-index --add --info-only one &&
+       git write-tree --missing-ok >tree.missing &&
+       git ls-tree $(cat tree.missing) >top.missing &&
+       git ls-tree -r $(cat tree.missing) >all.missing &&
+       echo one >one &&
+       git add one &&
+       git write-tree >tree &&
+       git ls-tree $(cat tree) >top &&
+       git ls-tree -r $(cat tree) >all &&
+       test_tick &&
+       git commit -q -m one &&
+       H=$(git rev-parse HEAD) &&
+       git update-index --add --cacheinfo 160000 $H sub &&
+       test_tick &&
+       git commit -q -m two &&
+       git rev-parse HEAD^{tree} >tree.withsub &&
+       git ls-tree HEAD >top.withsub &&
+       git ls-tree -r HEAD >all.withsub
+'
+
+test_expect_success 'ls-tree piped to mktree (1)' '
+       git mktree <top >actual &&
+       test_cmp tree actual
+'
+
+test_expect_success 'ls-tree piped to mktree (2)' '
+       git mktree <top.withsub >actual &&
+       test_cmp tree.withsub actual
+'
+
+test_expect_success 'ls-tree output in wrong order given to mktree (1)' '
+       perl -e "print reverse <>" <top |
+       git mktree >actual &&
+       test_cmp tree actual
+'
+
+test_expect_success 'ls-tree output in wrong order given to mktree (2)' '
+       perl -e "print reverse <>" <top.withsub |
+       git mktree >actual &&
+       test_cmp tree.withsub actual
+'
+
+test_expect_success 'allow missing object with --missing' '
+       git mktree --missing <top.missing >actual &&
+       test_cmp tree.missing actual
+'
+
+test_expect_failure 'mktree reads ls-tree -r output (1)' '
+       git mktree <all >actual &&
+       test_cmp tree actual
+'
+
+test_expect_failure 'mktree reads ls-tree -r output (2)' '
+       git mktree <all.withsub >actual &&
+       test_cmp tree.withsub actual
+'
+
+test_done
index 0de613dc53d85c01f6d122834e094503a2736507..9b3fa2bdcd9bebae46a000ffe9c2059cea5a93fd 100755 (executable)
@@ -276,6 +276,9 @@ test_expect_success 'fail if the index has unresolved entries' '
 
        test_must_fail git merge "$c5" &&
        test_must_fail git merge "$c5" 2> out &&
+       grep "You have not concluded your merge" out &&
+       rm -f .git/MERGE_HEAD &&
+       test_must_fail git merge "$c5" 2> out &&
        grep "You are in the middle of a conflicted merge" out
 
 '
index 6e391a370208c9a0a6e73facc87f6a0e5081b929..7f62bfb9ddbc4b28017be529d73c7ece29424fec 100755 (executable)
@@ -41,9 +41,40 @@ test_expect_success \
      git tag topic
 '
 
+test_expect_success 'rebase on dirty worktree' '
+     echo dirty >> A &&
+     test_must_fail git rebase master'
+
+test_expect_success 'rebase on dirty cache' '
+     git add A &&
+     test_must_fail git rebase master'
+
 test_expect_success 'rebase against master' '
+     git reset --hard HEAD &&
      git rebase master'
 
+test_expect_success 'rebase against master twice' '
+     git rebase master 2>err &&
+     grep "Current branch my-topic-branch is up to date" err
+'
+
+test_expect_success 'rebase against master twice with --force' '
+     git rebase --force-rebase master >out &&
+     grep "Current branch my-topic-branch is up to date, rebase forced" out
+'
+
+test_expect_success 'rebase against master twice from another branch' '
+     git checkout my-topic-branch^ &&
+     git rebase master my-topic-branch 2>err &&
+     grep "Current branch my-topic-branch is up to date" err
+'
+
+test_expect_success 'rebase fast-forward to master' '
+     git checkout my-topic-branch^ &&
+     git rebase my-topic-branch 2>err &&
+     grep "Fast-forwarded HEAD to my-topic-branch" err
+'
+
 test_expect_success \
     'the rebase operation should not have destroyed author information' \
     '! (git log | grep "Author:" | grep "<>")'
index 050de42ef4148a730c30520ccaad5e9871e536bd..6ae5a2cd9511b83ad1d263c0ec0a374fc4bfc981 100755 (executable)
@@ -221,6 +221,19 @@ test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
        test_must_fail git add --verbose . &&
        ! ( git ls-files foo1 | grep foo1 )
 '
+rm -f foo2
+
+test_expect_success '--no-ignore-errors overrides config' '
+       git config add.ignore-errors 1 &&
+       git reset --hard &&
+       date >foo1 &&
+       date >foo2 &&
+       chmod 0 foo2 &&
+       test_must_fail git add --verbose --no-ignore-errors . &&
+       ! ( git ls-files foo1 | grep foo1 ) &&
+       git config add.ignore-errors 0
+'
+rm -f foo2
 
 test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
        git reset --hard &&
@@ -230,4 +243,16 @@ test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
        ! ( git ls-files foobar | grep foobar )
 '
 
+test_expect_success 'git add to resolve conflicts on otherwise ignored path' '
+       git reset --hard &&
+       H=$(git rev-parse :1/2/a) &&
+       (
+               echo "100644 $H 1       track-this"
+               echo "100644 $H 3       track-this"
+       ) | git update-index --index-info &&
+       echo track-this >>.gitignore &&
+       echo resolved >track-this &&
+       git add track-this
+'
+
 test_done
diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh
new file mode 100755 (executable)
index 0000000..4ee47cc
--- /dev/null
@@ -0,0 +1,121 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='add -e basic tests'
+. ./test-lib.sh
+
+
+cat > file << EOF
+LO, praise of the prowess of people-kings
+of spear-armed Danes, in days long sped,
+we have heard, and what honor the athelings won!
+Oft Scyld the Scefing from squadroned foes,
+from many a tribe, the mead-bench tore,
+awing the earls. Since erst he lay
+friendless, a foundling, fate repaid him:
+for he waxed under welkin, in wealth he throve,
+till before him the folk, both far and near,
+who house by the whale-path, heard his mandate,
+gave him gifts:  a good king he!
+EOF
+
+cat > second-part << EOF
+To him an heir was afterward born,
+a son in his halls, whom heaven sent
+to favor the folk, feeling their woe
+that erst they had lacked an earl for leader
+so long a while; the Lord endowed him,
+the Wielder of Wonder, with world's renown.
+EOF
+
+test_expect_success 'setup' '
+
+       git add file &&
+       test_tick &&
+       git commit -m initial file
+
+'
+
+cat > expected-patch << EOF
+diff --git a/file b/file
+index b9834b5..9020acb 100644
+--- a/file
++++ b/file
+@@ -1,11 +1,6 @@
+-LO, praise of the prowess of people-kings
+-of spear-armed Danes, in days long sped,
+-we have heard, and what honor the athelings won!
+-Oft Scyld the Scefing from squadroned foes,
+-from many a tribe, the mead-bench tore,
+-awing the earls. Since erst he lay
+-friendless, a foundling, fate repaid him:
+-for he waxed under welkin, in wealth he throve,
+-till before him the folk, both far and near,
+-who house by the whale-path, heard his mandate,
+-gave him gifts:  a good king he!
++To him an heir was afterward born,
++a son in his halls, whom heaven sent
++to favor the folk, feeling their woe
++that erst they had lacked an earl for leader
++so long a while; the Lord endowed him,
++the Wielder of Wonder, with world's renown.
+EOF
+
+cat > patch << EOF
+diff --git a/file b/file
+index b9834b5..ef6e94c 100644
+--- a/file
++++ b/file
+@@ -3,1 +3,333 @@ of spear-armed Danes, in days long sped,
+ we have heard, and what honor the athelings won!
++
+ Oft Scyld the Scefing from squadroned foes,
+@@ -2,7 +1,5 @@ awing the earls. Since erst he lay
+ friendless, a foundling, fate repaid him:
++
+ for he waxed under welkin, in wealth he throve,
+EOF
+
+cat > expected << EOF
+diff --git a/file b/file
+index b9834b5..ef6e94c 100644
+--- a/file
++++ b/file
+@@ -1,10 +1,12 @@
+ LO, praise of the prowess of people-kings
+ of spear-armed Danes, in days long sped,
+ we have heard, and what honor the athelings won!
++
+ Oft Scyld the Scefing from squadroned foes,
+ from many a tribe, the mead-bench tore,
+ awing the earls. Since erst he lay
+ friendless, a foundling, fate repaid him:
++
+ for he waxed under welkin, in wealth he throve,
+ till before him the folk, both far and near,
+ who house by the whale-path, heard his mandate,
+EOF
+
+echo "#!$SHELL_PATH" >fake-editor.sh
+cat >> fake-editor.sh <<\EOF
+mv -f "$1" orig-patch &&
+mv -f patch "$1"
+EOF
+
+test_set_editor "$(pwd)/fake-editor.sh"
+chmod a+x fake-editor.sh
+
+test_expect_success 'add -e' '
+
+       cp second-part file &&
+       git add -e &&
+       test_cmp second-part file &&
+       test_cmp orig-patch expected-patch &&
+       git diff --cached > out &&
+       test_cmp out expected
+
+'
+
+test_done
index 784c31aec99d90b69186079ddb66350d9f4a8827..256c4c970145aa9f59e58ee1b0da4c6281b6d9e5 100755 (executable)
@@ -9,7 +9,15 @@ test_description='commit and log output encodings'
 
 compare_with () {
        git show -s $1 | sed -e '1,/^$/d' -e 's/^    //' >current &&
-       test_cmp current "$2"
+       case "$3" in
+       '')
+               test_cmp "$2" current ;;
+       ?*)
+               iconv -f "$3" -t UTF-8 >current.utf8 <current &&
+               iconv -f "$3" -t UTF-8 >expect.utf8 <"$2" &&
+               test_cmp expect.utf8 current.utf8
+               ;;
+       esac
 }
 
 test_expect_success setup '
@@ -26,7 +34,7 @@ test_expect_success 'no encoding header for base case' '
        test z = "z$E"
 '
 
-for H in ISO-8859-1 EUCJP ISO-2022-JP
+for H in ISO8859-1 eucJP ISO-2022-JP
 do
        test_expect_success "$H setup" '
                git config i18n.commitencoding $H &&
@@ -36,7 +44,7 @@ do
        '
 done
 
-for H in ISO-8859-1 EUCJP ISO-2022-JP
+for H in ISO8859-1 eucJP ISO-2022-JP
 do
        test_expect_success "check encoding header for $H" '
                E=$(git cat-file commit '$H' | sed -ne "s/^encoding //p") &&
@@ -53,14 +61,14 @@ test_expect_success 'config to remove customization' '
        else
                test z = "z$Z"
        fi &&
-       git config i18n.commitencoding utf-8
+       git config i18n.commitencoding UTF-8
 '
 
-test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
-       compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
+test_expect_success 'ISO8859-1 should be shown in UTF-8 now' '
+       compare_with ISO8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
 '
 
-for H in EUCJP ISO-2022-JP
+for H in eucJP ISO-2022-JP
 do
        test_expect_success "$H should be shown in UTF-8 now" '
                compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
@@ -78,7 +86,7 @@ test_expect_success 'config to add customization' '
        fi
 '
 
-for H in ISO-8859-1 EUCJP ISO-2022-JP
+for H in ISO8859-1 eucJP ISO-2022-JP
 do
        test_expect_success "$H should be shown in itself now" '
                git config i18n.commitencoding '$H' &&
@@ -87,32 +95,38 @@ do
 done
 
 test_expect_success 'config to tweak customization' '
-       git config i18n.logoutputencoding utf-8
+       git config i18n.logoutputencoding UTF-8
 '
 
-test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
-       compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
+test_expect_success 'ISO8859-1 should be shown in UTF-8 now' '
+       compare_with ISO8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
 '
 
-for H in EUCJP ISO-2022-JP
+for H in eucJP ISO-2022-JP
 do
        test_expect_success "$H should be shown in UTF-8 now" '
                compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
        '
 done
 
-for J in EUCJP ISO-2022-JP
+for J in eucJP ISO-2022-JP
 do
+       if test "$J" = ISO-2022-JP
+       then
+               ICONV=$J
+       else
+               ICONV=
+       fi
        git config i18n.logoutputencoding $J
-       for H in EUCJP ISO-2022-JP
+       for H in eucJP ISO-2022-JP
        do
                test_expect_success "$H should be shown in $J now" '
-                       compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt
+                       compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt $ICONV
                '
        done
 done
 
-for H in ISO-8859-1 EUCJP ISO-2022-JP
+for H in ISO8859-1 eucJP ISO-2022-JP
 do
        test_expect_success "No conversion with $H" '
                compare_with "--encoding=none '$H'" "$TEST_DIRECTORY"/t3900/'$H'.txt
diff --git a/t/t3900/EUCJP.txt b/t/t3900/EUCJP.txt
deleted file mode 100644 (file)
index 546f2aa..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-¤Ï¤ì¤Ò¤Û¤Õ
-
-¤·¤Æ¤¤¤ë¤Î¤¬¡¢¤¤¤ë¤Î¤Ç¡£
-ßÀÉͤۤì¤×¤ê¤Ý¤ì¤Þ¤Ó¤°¤ê¤í¤Ø¡£
diff --git a/t/t3900/ISO-8859-1.txt b/t/t3900/ISO-8859-1.txt
deleted file mode 100644 (file)
index 7cbef0e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-ÄËÑÏÖ
-
-Ábçdèfg
diff --git a/t/t3900/ISO8859-1.txt b/t/t3900/ISO8859-1.txt
new file mode 100644 (file)
index 0000000..7cbef0e
--- /dev/null
@@ -0,0 +1,3 @@
+ÄËÑÏÖ
+
+Ábçdèfg
diff --git a/t/t3900/eucJP.txt b/t/t3900/eucJP.txt
new file mode 100644 (file)
index 0000000..546f2aa
--- /dev/null
@@ -0,0 +1,4 @@
+¤Ï¤ì¤Ò¤Û¤Õ
+
+¤·¤Æ¤¤¤ë¤Î¤¬¡¢¤¤¤ë¤Î¤Ç¡£
+ßÀÉͤۤì¤×¤ê¤Ý¤ì¤Þ¤Ó¤°¤ê¤í¤Ø¡£
index 7655da3f8d5e68f293ae5afe2d58dd41b1396f37..31a5770b34466c9ed52a9f00bac27b71fd6e0deb 100755 (executable)
@@ -17,9 +17,9 @@ check_encoding () {
                git cat-file commit HEAD~$j |
                case "$header" in
                8859)
-                       grep "^encoding ISO-8859-1" ;;
+                       grep "^encoding ISO8859-1" ;;
                *)
-                       ! grep "^encoding ISO-8859-1" ;;
+                       grep "^encoding ISO8859-1"; test "$?" != 0 ;;
                esac || {
                        bad=1
                        break
@@ -55,7 +55,7 @@ test_expect_success setup '
        git commit -s -m "Second on side" &&
 
        # the second one on the side branch is ISO-8859-1
-       git config i18n.commitencoding ISO-8859-1 &&
+       git config i18n.commitencoding ISO8859-1 &&
        # use author and committer name in ISO-8859-1 to match it.
        . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
        test_tick &&
@@ -68,14 +68,14 @@ test_expect_success setup '
 '
 
 test_expect_success 'format-patch output (ISO-8859-1)' '
-       git config i18n.logoutputencoding ISO-8859-1 &&
+       git config i18n.logoutputencoding ISO8859-1 &&
 
        git format-patch --stdout master..HEAD^ >out-l1 &&
        git format-patch --stdout HEAD^ >out-l2 &&
-       grep "^Content-Type: text/plain; charset=ISO-8859-1" out-l1 &&
-       grep "^From: =?ISO-8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l1 &&
-       grep "^Content-Type: text/plain; charset=ISO-8859-1" out-l2 &&
-       grep "^From: =?ISO-8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l2
+       grep "^Content-Type: text/plain; charset=ISO8859-1" out-l1 &&
+       grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l1 &&
+       grep "^Content-Type: text/plain; charset=ISO8859-1" out-l2 &&
+       grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l2
 '
 
 test_expect_success 'format-patch output (UTF-8)' '
@@ -110,7 +110,7 @@ test_expect_success 'rebase (U/U)' '
 
 test_expect_success 'rebase (U/L)' '
        git config i18n.commitencoding UTF-8 &&
-       git config i18n.logoutputencoding ISO-8859-1 &&
+       git config i18n.logoutputencoding ISO8859-1 &&
        . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        git reset --hard side &&
@@ -121,8 +121,8 @@ test_expect_success 'rebase (U/L)' '
 
 test_expect_success 'rebase (L/L)' '
        # In this test we want ISO-8859-1 encoded commits as the result
-       git config i18n.commitencoding ISO-8859-1 &&
-       git config i18n.logoutputencoding ISO-8859-1 &&
+       git config i18n.commitencoding ISO8859-1 &&
+       git config i18n.logoutputencoding ISO8859-1 &&
        . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard side &&
@@ -134,7 +134,7 @@ test_expect_success 'rebase (L/L)' '
 test_expect_success 'rebase (L/U)' '
        # This is pathological -- use UTF-8 as intermediate form
        # to get ISO-8859-1 results.
-       git config i18n.commitencoding ISO-8859-1 &&
+       git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
        . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
@@ -162,8 +162,8 @@ test_expect_success 'cherry-pick(U/U)' '
 test_expect_success 'cherry-pick(L/L)' '
        # Both the commitencoding and logoutputencoding is set to ISO-8859-1
 
-       git config i18n.commitencoding ISO-8859-1 &&
-       git config i18n.logoutputencoding ISO-8859-1 &&
+       git config i18n.commitencoding ISO8859-1 &&
+       git config i18n.logoutputencoding ISO8859-1 &&
        . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard master &&
@@ -178,7 +178,7 @@ test_expect_success 'cherry-pick(U/L)' '
        # Commitencoding is set to UTF-8 but logoutputencoding is ISO-8859-1
 
        git config i18n.commitencoding UTF-8 &&
-       git config i18n.logoutputencoding ISO-8859-1 &&
+       git config i18n.logoutputencoding ISO8859-1 &&
        . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        git reset --hard master &&
@@ -193,7 +193,7 @@ test_expect_success 'cherry-pick(L/U)' '
        # Again, the commitencoding is set to ISO-8859-1 but
        # logoutputencoding is set to UTF-8.
 
-       git config i18n.commitencoding ISO-8859-1 &&
+       git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
        . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
@@ -218,7 +218,7 @@ test_expect_success 'rebase --merge (U/U)' '
 
 test_expect_success 'rebase --merge (U/L)' '
        git config i18n.commitencoding UTF-8 &&
-       git config i18n.logoutputencoding ISO-8859-1 &&
+       git config i18n.logoutputencoding ISO8859-1 &&
        . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        git reset --hard side &&
@@ -229,8 +229,8 @@ test_expect_success 'rebase --merge (U/L)' '
 
 test_expect_success 'rebase --merge (L/L)' '
        # In this test we want ISO-8859-1 encoded commits as the result
-       git config i18n.commitencoding ISO-8859-1 &&
-       git config i18n.logoutputencoding ISO-8859-1 &&
+       git config i18n.commitencoding ISO8859-1 &&
+       git config i18n.logoutputencoding ISO8859-1 &&
        . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard side &&
@@ -242,7 +242,7 @@ test_expect_success 'rebase --merge (L/L)' '
 test_expect_success 'rebase --merge (L/U)' '
        # This is pathological -- use UTF-8 as intermediate form
        # to get ISO-8859-1 results.
-       git config i18n.commitencoding ISO-8859-1 &&
+       git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
        . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
index 12da8ac07de4423cf4736aced96bab8150f83ef0..954210ea907cb04f6113d82194c0753019270d8a 100644 (file)
@@ -1,12 +1,12 @@
 $ git log --decorate --all
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (master)
 Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
     Merge branch 'side'
 
-commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side)
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:03:00 2006 +0000
 
@@ -26,7 +26,7 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 
-commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (initial)
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:00:00 2006 +0000
 
index 0720001281db6aeb5a3b6bb46cd6914ad7d78d33..4ea42e00dad9bf15e221e6eac5d91748cb0a07e7 100755 (executable)
@@ -136,6 +136,15 @@ test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
        GIT_EXTERNAL_DIFF=echo git diff
 '
 
+test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
+       touch file.ext &&
+       git add file.ext &&
+       echo with extension > file.ext &&
+       GIT_EXTERNAL_DIFF=echo git diff file.ext | grep ......_file\.ext &&
+       git update-index --force-remove file.ext &&
+       rm file.ext
+'
+
 echo "#!$SHELL_PATH" >fake-diff.sh
 cat >> fake-diff.sh <<\EOF
 cat $2 >> crlfed.txt
index 64502e2be762a82b65a72af6847d5a3b79303d45..aad3894ad4c542d4ab894c2d57a035a89eb88258 100755 (executable)
@@ -324,14 +324,12 @@ cat > expect <<\EOF
 * | | |   Merge branch 'side'
 |\ \ \ \
 | * | | | side-2
-| | | |/
-| | |/|
+| | |_|/
 | |/| |
 | * | | side-1
 * | | | Second
 * | | | sixth
-| | |/
-| |/|
+| |_|/
 |/| |
 * | | fifth
 * | | fourth
index 3ca2470da2ef8e082bfbb210ee9acf651b6588d6..1fc224810da6f02fbb4407bd030ab5aee3f3ef50 100644 (file)
@@ -1,48 +1,48 @@
 From nobody Mon Sep 17 00:00:00 2001
 From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>
-To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
-CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>
-Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
- =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=
+To: =?ISO8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
+CC: =?ISO8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>
+Subject: =?ISO8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
+ =?ISO8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=
 
 From nobody Mon Sep 17 00:00:00 2001
-From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>
+From: =?ISO8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>
 To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se
 Subject: Time for ISO 10646?
 
 From nobody Mon Sep 17 00:00:00 2001
 To: Dave Crocker <dcrocker@mordor.stanford.edu>
 Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se
-From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>
+From: =?ISO8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>
 Subject: Re: RFC-HDR care and feeding
 
 From nobody Mon Sep 17 00:00:00 2001
 From: Nathaniel Borenstein <nsb@thumper.bellcore.com>
-      (=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=)
+      (=?ISO8859-8?b?7eXs+SDv4SDp7Oj08A==?=)
 To: Greg Vaudreuil <gvaudre@NRI.Reston.VA.US>, Ned Freed
    <ned@innosoft.com>, Keith Moore <moore@cs.utk.edu>
 Subject: Test of new header generator
 MIME-Version: 1.0
-Content-type: text/plain; charset=ISO-8859-1
+Content-type: text/plain; charset=ISO8859-1
 
 From nobody Mon Sep 17 00:00:00 2001
-Subject: (=?ISO-8859-1?Q?a?=)
+Subject: (=?ISO8859-1?Q?a?=)
 
 From nobody Mon Sep 17 00:00:00 2001
-Subject: (=?ISO-8859-1?Q?a?= b)
+Subject: (=?ISO8859-1?Q?a?= b)
 
 From nobody Mon Sep 17 00:00:00 2001
-Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)
+Subject: (=?ISO8859-1?Q?a?= =?ISO8859-1?Q?b?=)
 
 From nobody Mon Sep 17 00:00:00 2001
-Subject: (=?ISO-8859-1?Q?a?=  =?ISO-8859-1?Q?b?=)
+Subject: (=?ISO8859-1?Q?a?=  =?ISO8859-1?Q?b?=)
 
 From nobody Mon Sep 17 00:00:00 2001
-Subject: (=?ISO-8859-1?Q?a?=
-    =?ISO-8859-1?Q?b?=)
+Subject: (=?ISO8859-1?Q?a?=
+    =?ISO8859-1?Q?b?=)
 
 From nobody Mon Sep 17 00:00:00 2001
-Subject: (=?ISO-8859-1?Q?a_b?=)
+Subject: (=?ISO8859-1?Q?a_b?=)
 
 From nobody Mon Sep 17 00:00:00 2001
-Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)
+Subject: (=?ISO8859-1?Q?a?= =?ISO8859-2?Q?_b?=)
index c5ad206b40e1fcf79019cebdfd848d72c17cefcc..c3074ac5736fe266e901b4da3b060208cd71406e 100644 (file)
@@ -99,7 +99,7 @@ index 9123cdc..918dcf8 100644
 From nobody Sat Aug 27 23:07:49 2005
 Path: news.gmane.org!not-for-mail
 Message-ID: <20050721.091036.01119516.yoshfuji@linux-ipv6.org>
-From: YOSHIFUJI Hideaki / =?iso-2022-jp?B?GyRCNUhGIzFRTEAbKEI=?= 
+From: YOSHIFUJI Hideaki / =?ISO-2022-JP?B?GyRCNUhGIzFRTEAbKEI=?= 
        <yoshfuji@linux-ipv6.org>
 Newsgroups: gmane.comp.version-control.git
 Subject: [PATCH 1/2] GIT: Try all addresses for given remote name
@@ -218,7 +218,7 @@ GPG-FP  : 9022 65EB 1ECF 3AD1 0BDF  80D8 4807 F894 E062 0EEA
 From nobody Sat Aug 27 23:07:49 2005
 Path: news.gmane.org!not-for-mail
 Message-ID: <u5tacjjdpxq.fsf@lysator.liu.se>
-From: =?iso-8859-1?Q?David_K=E5gedal?= <davidk@lysator.liu.se>
+From: =?ISO8859-1?Q?David_K=E5gedal?= <davidk@lysator.liu.se>
 Newsgroups: gmane.comp.version-control.git
 Subject: [PATCH] Fixed two bugs in git-cvsimport-script.
 Date: Mon, 15 Aug 2005 20:18:25 +0200
@@ -226,7 +226,7 @@ Lines: 83
 Approved: news@gmane.org
 NNTP-Posting-Host: main.gmane.org
 Mime-Version: 1.0
-Content-Type: text/plain; charset=iso-8859-1
+Content-Type: text/plain; charset=ISO8859-1
 Content-Transfer-Encoding: QUOTED-PRINTABLE
 X-Trace: sea.gmane.org 1124130247 31839 80.91.229.2 (15 Aug 2005 18:24:07 GMT)
 X-Complaints-To: usenet@sea.gmane.org
@@ -476,7 +476,7 @@ MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="=-=-="
 
 --=-=-=
-Content-Type: text/plain; charset=iso-8859-15
+Content-Type: text/plain; charset=ISO8859-15
 Content-Transfer-Encoding: quoted-printable
 
 Here comes a commit log message, and
index c450f33f333e6f1c367f8f350dfd78f8f44a0fee..a8c2ca2a78dd54f69230cb443eff1d6e82336f0a 100755 (executable)
@@ -3,9 +3,8 @@
 # Copyright (c) 2005 Johannes Schindelin
 #
 
-test_description='Testing multi_ack pack fetching
+test_description='Testing multi_ack pack fetching'
 
-'
 . ./test-lib.sh
 
 # Test fetch-pack/upload-pack pair.
@@ -13,77 +12,60 @@ test_description='Testing multi_ack pack fetching
 # Some convenience functions
 
 add () {
-       name=$1
-       text="$@"
-       branch=`echo $name | sed -e 's/^\(.\).*$/\1/'`
-       parents=""
+       name=$1 &&
+       text="$@" &&
+       branch=`echo $name | sed -e 's/^\(.\).*$/\1/'` &&
+       parents="" &&
 
-       shift
+       shift &&
        while test $1; do
-               parents="$parents -p $1"
+               parents="$parents -p $1" &&
                shift
-       done
+       done &&
 
-       echo "$text" > test.txt
-       git update-index --add test.txt
-       tree=$(git write-tree)
+       echo "$text" > test.txt &&
+       git update-index --add test.txt &&
+       tree=$(git write-tree) &&
        # make sure timestamps are in correct order
-       sec=$(($sec+1))
-       commit=$(echo "$text" | GIT_AUTHOR_DATE=$sec \
-               git commit-tree $tree $parents 2>>log2.txt)
-       eval "$name=$commit; export $name"
-       echo $commit > .git/refs/heads/$branch
+       test_tick &&
+       commit=$(echo "$text" | git commit-tree $tree $parents) &&
+       eval "$name=$commit; export $name" &&
+       echo $commit > .git/refs/heads/$branch &&
        eval ${branch}TIP=$commit
 }
 
-count_objects () {
-       ls .git/objects/??/* 2>>log2.txt | wc -l | tr -d " "
-}
-
-test_expect_object_count () {
-       message=$1
-       count=$2
-
-       output="$(count_objects)"
-       test_expect_success \
-               "new object count $message" \
-               "test $count = $output"
-}
-
 pull_to_client () {
-       number=$1
-       heads=$2
-       count=$3
-       no_strict_count_check=$4
-
-       cd client
-       test_expect_success "$number pull" \
-               "git fetch-pack -k -v .. $heads"
-       case "$heads" in *A*) echo $ATIP > .git/refs/heads/A;; esac
-       case "$heads" in *B*) echo $BTIP > .git/refs/heads/B;; esac
-       git symbolic-ref HEAD refs/heads/`echo $heads | sed -e 's/^\(.\).*$/\1/'`
-
-       test_expect_success "fsck" 'git fsck --full > fsck.txt 2>&1'
-
-       test_expect_success 'check downloaded results' \
-       'mv .git/objects/pack/pack-* . &&
-        p=`ls -1 pack-*.pack` &&
-        git unpack-objects <$p &&
-        git fsck --full'
-
-       test_expect_success "new object count after $number pull" \
-       'idx=`echo pack-*.idx` &&
-        pack_count=`git show-index <$idx | wc -l` &&
-        test $pack_count = $count'
-       test -z "$pack_count" && pack_count=0
-       if [ -z "$no_strict_count_check" ]; then
-               test_expect_success "minimal count" "test $count = $pack_count"
-       else
-               test $count != $pack_count && \
-                       echo "WARNING: $pack_count objects transmitted, only $count of which were needed"
-       fi
-       rm -f pack-*
-       cd ..
+       number=$1 &&
+       heads=$2 &&
+       count=$3 &&
+       test_expect_success "$number pull" '
+               (
+                       cd client &&
+                       git fetch-pack -k -v .. $heads &&
+
+                       case "$heads" in
+                           *A*)
+                                   echo $ATIP > .git/refs/heads/A;;
+                       esac &&
+                       case "$heads" in *B*)
+                           echo $BTIP > .git/refs/heads/B;;
+                       esac &&
+                       git symbolic-ref HEAD refs/heads/`echo $heads \
+                               | sed -e "s/^\(.\).*$/\1/"` &&
+
+                       git fsck --full &&
+
+                       mv .git/objects/pack/pack-* . &&
+                       p=`ls -1 pack-*.pack` &&
+                       git unpack-objects <$p &&
+                       git fsck --full &&
+
+                       idx=`echo pack-*.idx` &&
+                       pack_count=`git show-index <$idx | wc -l` &&
+                       test $pack_count = $count &&
+                       rm -f pack-*
+               )
+       '
 }
 
 # Here begins the actual testing
@@ -94,89 +76,129 @@ pull_to_client () {
 
 # client pulls A20, B1. Then tracks only B. Then pulls A.
 
-(
+test_expect_success 'setup' '
        mkdir client &&
-       cd client &&
-       git init 2>> log2.txt &&
-       git config transfer.unpacklimit 0
-)
-
-add A1
-
-prev=1; cur=2; while [ $cur -le 10 ]; do
-       add A$cur $(eval echo \$A$prev)
-       prev=$cur
-       cur=$(($cur+1))
-done
-
-add B1 $A1
-
-echo $ATIP > .git/refs/heads/A
-echo $BTIP > .git/refs/heads/B
-git symbolic-ref HEAD refs/heads/B
+       (
+               cd client &&
+               git init &&
+               git config transfer.unpacklimit 0
+       ) &&
+       add A1 &&
+       prev=1 &&
+       cur=2 &&
+       while [ $cur -le 10 ]; do
+               add A$cur $(eval echo \$A$prev) &&
+               prev=$cur &&
+               cur=$(($cur+1))
+       done &&
+       add B1 $A1
+       echo $ATIP > .git/refs/heads/A &&
+       echo $BTIP > .git/refs/heads/B &&
+       git symbolic-ref HEAD refs/heads/B
+'
 
 pull_to_client 1st "B A" $((11*3))
 
-add A11 $A10
-
-prev=1; cur=2; while [ $cur -le 65 ]; do
-       add B$cur $(eval echo \$B$prev)
-       prev=$cur
-       cur=$(($cur+1))
-done
+test_expect_success 'post 1st pull setup' '
+       add A11 $A10 &&
+       prev=1 &&
+       cur=2 &&
+       while [ $cur -le 65 ]; do
+               add B$cur $(eval echo \$B$prev) &&
+               prev=$cur &&
+               cur=$(($cur+1))
+       done
+'
 
 pull_to_client 2nd "B" $((64*3))
 
-pull_to_client 3rd "A" $((1*3)) # old fails
-
-test_expect_success "clone shallow" 'git clone --depth 2 "file://$(pwd)/." shallow'
+pull_to_client 3rd "A" $((1*3))
 
-(cd shallow; git count-objects -v) > count.shallow
-
-test_expect_success "clone shallow object count" \
-       "test \"in-pack: 18\" = \"$(grep in-pack count.shallow)\""
-
-count_output () {
-       sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/^size-pack:/d' -e '/: 0$/d' "$1"
-}
+test_expect_success 'clone shallow' '
+       git clone --depth 2 "file://$(pwd)/." shallow
+'
 
-test_expect_success "clone shallow object count (part 2)" '
-       test -z "$(count_output count.shallow)"
+test_expect_success 'clone shallow object count' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow &&
+       grep "^in-pack: 18" count.shallow
 '
 
-test_expect_success "fsck in shallow repo" \
-       "(cd shallow; git fsck --full)"
+test_expect_success 'clone shallow object count (part 2)' '
+       sed -e "/^in-pack:/d" -e "/^packs:/d" -e "/^size-pack:/d" \
+           -e "/: 0$/d" count.shallow > count_output &&
+       ! test -s count_output
+'
 
-#test_done; exit
+test_expect_success 'fsck in shallow repo' '
+       (
+               cd shallow &&
+               git fsck --full
+       )
+'
 
-add B66 $B65
-add B67 $B66
+test_expect_success 'add two more' '
+       add B66 $B65 &&
+       add B67 $B66
+'
 
-test_expect_success "pull in shallow repo" \
-       "(cd shallow; git pull .. B)"
+test_expect_success 'pull in shallow repo' '
+       (
+               cd shallow &&
+               git pull .. B
+       )
+'
 
-(cd shallow; git count-objects -v) > count.shallow
-test_expect_success "clone shallow object count" \
-       "test \"count: 6\" = \"$(grep count count.shallow)\""
+test_expect_success 'clone shallow object count' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow &&
+       grep "^count: 6" count.shallow
+'
 
-add B68 $B67
-add B69 $B68
+test_expect_success 'add two more (part 2)' '
+       add B68 $B67 &&
+       add B69 $B68
+'
 
-test_expect_success "deepening pull in shallow repo" \
-       "(cd shallow; git pull --depth 4 .. B)"
+test_expect_success 'deepening pull in shallow repo' '
+       (
+               cd shallow &&
+               git pull --depth 4 .. B
+       )
+'
 
-(cd shallow; git count-objects -v) > count.shallow
-test_expect_success "clone shallow object count" \
-       "test \"count: 12\" = \"$(grep count count.shallow)\""
+test_expect_success 'clone shallow object count' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow &&
+       grep "^count: 12" count.shallow
+'
 
-test_expect_success "deepening fetch in shallow repo" \
-       "(cd shallow; git fetch --depth 4 .. A:A)"
+test_expect_success 'deepening fetch in shallow repo' '
+       (
+               cd shallow &&
+               git fetch --depth 4 .. A:A
+       )
+'
 
-(cd shallow; git count-objects -v) > count.shallow
-test_expect_success "clone shallow object count" \
-       "test \"count: 18\" = \"$(grep count count.shallow)\""
+test_expect_success 'clone shallow object count' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow &&
+       grep "^count: 18" count.shallow
+'
 
-test_expect_success "pull in shallow repo with missing merge base" \
-       "(cd shallow && test_must_fail git pull --depth 4 .. A)"
+test_expect_success 'pull in shallow repo with missing merge base' '
+       (
+               cd shallow &&
+               test_must_fail git pull --depth 4 .. A
+       )
+'
 
 test_done
index 725771fac167ea5aac8cf65b916c96918b0f5e0d..c5a2e66a09b9a871fb97142a58de51097a685ae0 100755 (executable)
@@ -92,20 +92,34 @@ test_expect_success '--rebase with rebased upstream' '
 
        git remote add -f me . &&
        git checkout copy &&
+       git tag copy-orig &&
        git reset --hard HEAD^ &&
        echo conflicting modification > file &&
        git commit -m conflict file &&
        git checkout to-rebase &&
        echo file > file2 &&
        git commit -m to-rebase file2 &&
+       git tag to-rebase-orig &&
        git pull --rebase me copy &&
        test "conflicting modification" = "$(cat file)" &&
        test file = $(cat file2)
 
 '
 
+test_expect_success '--rebase with rebased default upstream' '
+
+       git update-ref refs/remotes/me/copy copy-orig &&
+       git checkout --track -b to-rebase2 me/copy &&
+       git reset --hard to-rebase-orig &&
+       git pull --rebase &&
+       test "conflicting modification" = "$(cat file)" &&
+       test file = $(cat file2)
+
+'
+
 test_expect_success 'pull --rebase dies early with dirty working directory' '
 
+       git checkout to-rebase &&
        git update-ref refs/remotes/me/copy copy^ &&
        COPY=$(git rev-parse --verify me/copy) &&
        git rebase --onto $COPY copy &&
index 5fe479e1c2362ba05d059805d412b605aa7a83aa..f4a2cf6c171a6356804f61ac6f5707dd587515e5 100755 (executable)
@@ -67,6 +67,42 @@ test_expect_success ' push to remote repository with unpacked refs' '
         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/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/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/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 rm origin &&
+        git reflog expire --expire=0 --all &&
+        git prune &&
+        git push -f -v $HTTPD_URL/test_repo_packed.git master)
+'
+
 test_expect_success 'create and delete remote branch' '
        cd "$ROOT_PATH"/test_repo_clone &&
        git checkout -b dev &&
index 05b1b62cb66956bca57e1fbacac534993637bdd9..0e69324652026bdeeb319c36782fe1130b0510a1 100755 (executable)
@@ -53,5 +53,13 @@ test_expect_success 'http remote detects correct HEAD' '
        )
 '
 
+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 &&
+       git --bare prune-packed &&
+       git clone $HTTPD_URL/repo_pack.git
+'
+
 stop_httpd
 test_done
index 54b7ea6505d8c189c6c557cffd3d6518df06fb73..4556cdd8d2ac5d0b5c0e49d232de9923695ee540 100755 (executable)
@@ -482,28 +482,17 @@ test_expect_success 'good merge bases when good and bad are siblings' '
        git bisect reset
 '
 
-check_trace() {
-       grep "$1" "$GIT_TRACE" | grep "\^$2" | grep "$3" >/dev/null
-}
-
 test_expect_success 'optimized merge base checks' '
-       GIT_TRACE="$(pwd)/trace.log" &&
-       export GIT_TRACE &&
        git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
        grep "merge base must be tested" my_bisect_log.txt &&
        grep "$HASH4" my_bisect_log.txt &&
-       check_trace "rev-list" "$HASH7" "$SIDE_HASH7" &&
        git bisect good > my_bisect_log2.txt &&
        test -f ".git/BISECT_ANCESTORS_OK" &&
        test "$HASH6" = $(git rev-parse --verify HEAD) &&
-       : > "$GIT_TRACE" &&
        git bisect bad > my_bisect_log3.txt &&
-       test_must_fail check_trace "rev-list" "$HASH6" "$SIDE_HASH7" &&
        git bisect good "$A_HASH" > my_bisect_log4.txt &&
        grep "merge base must be tested" my_bisect_log4.txt &&
-       test_must_fail test -f ".git/BISECT_ANCESTORS_OK" &&
-       check_trace "rev-list" "$HASH6" "$A_HASH" &&
-       unset GIT_TRACE
+       test_must_fail test -f ".git/BISECT_ANCESTORS_OK"
 '
 
 # This creates another side branch called "parallel" with some files
@@ -566,6 +555,18 @@ test_expect_success 'restricting bisection on one dir and a file' '
        grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
 '
 
+test_expect_success 'skipping away from skipped commit' '
+       git bisect start $PARA_HASH7 $HASH1 &&
+       para4=$(git rev-parse --verify HEAD) &&
+       test "$para4" = "$PARA_HASH4" &&
+        git bisect skip &&
+       hash7=$(git rev-parse --verify HEAD) &&
+       test "$hash7" = "$HASH7" &&
+        git bisect skip &&
+       hash3=$(git rev-parse --verify HEAD) &&
+       test "$hash3" = "$HASH3"
+'
+
 #
 #
 test_done
index 3d6db4d386a0a009892fac490818294664d188fa..00e1de9627e8e20f4d28e3122502a494d21899e5 100755 (executable)
@@ -74,5 +74,19 @@ test_expect_success 'status' '
        grep "have 1 and 1 different" actual
 '
 
+test_expect_success 'status when tracking lightweight tags' '
+       git checkout master &&
+       git tag light &&
+       git branch --track lighttrack light >actual &&
+       grep "set up to track" actual &&
+       git checkout lighttrack
+'
 
+test_expect_success 'status when tracking annotated tags' '
+       git checkout master &&
+       git tag -m heavy heavy &&
+       git branch --track heavytrack heavy >actual &&
+       grep "set up to track" actual &&
+       git checkout heavytrack
+'
 test_done
diff --git a/t/t7406-submodule-reference.sh b/t/t7406-submodule-reference.sh
new file mode 100755 (executable)
index 0000000..cc16d3f
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Red Hat Inc, Author: Michael S. Tsirkin (mst@redhat.com)
+#
+
+test_description='test clone --reference'
+. ./test-lib.sh
+
+base_dir=`pwd`
+
+U=$base_dir/UPLOAD_LOG
+
+test_expect_success 'preparing first repository' \
+'test_create_repo A && cd A &&
+echo first > file1 &&
+git add file1 &&
+git commit -m A-initial'
+
+cd "$base_dir"
+
+test_expect_success 'preparing second repository' \
+'git clone A B && cd B &&
+echo second > file2 &&
+git add file2 &&
+git commit -m B-addition &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'preparing supermodule' \
+'test_create_repo super && cd super &&
+echo file > file &&
+git add file &&
+git commit -m B-super-initial'
+
+cd "$base_dir"
+
+test_expect_success 'submodule add --reference' \
+'cd super && git submodule add --reference ../B "file://$base_dir/A" sub &&
+git commit -m B-super-added'
+
+cd "$base_dir"
+
+test_expect_success 'after add: existence of info/alternates' \
+'test `wc -l <super/sub/.git/objects/info/alternates` = 1'
+
+cd "$base_dir"
+
+test_expect_success 'that reference gets used with add' \
+'cd super/sub &&
+echo "0 objects, 0 kilobytes" > expected &&
+git count-objects > current &&
+diff expected current'
+
+cd "$base_dir"
+
+test_expect_success 'cloning supermodule' \
+'git clone super super-clone'
+
+cd "$base_dir"
+
+test_expect_success 'update with reference' \
+'cd super-clone && git submodule update --init --reference ../B'
+
+cd "$base_dir"
+
+test_expect_success 'after update: existence of info/alternates' \
+'test `wc -l <super-clone/sub/.git/objects/info/alternates` = 1'
+
+cd "$base_dir"
+
+test_expect_success 'that reference gets used with update' \
+'cd super-clone/sub &&
+echo "0 objects, 0 kilobytes" > expected &&
+git count-objects > current &&
+diff expected current'
+
+cd "$base_dir"
+
+test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
new file mode 100755 (executable)
index 0000000..0773fe4
--- /dev/null
@@ -0,0 +1,140 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Red Hat, Inc.
+#
+
+test_description='Test updating submodules
+
+This test verifies that "git submodule update" detaches the HEAD of the
+submodule and "git submodule update --rebase" does not detach the HEAD.
+'
+
+. ./test-lib.sh
+
+
+compare_head()
+{
+    sha_master=`git-rev-list --max-count=1 master`
+    sha_head=`git-rev-list --max-count=1 HEAD`
+
+    test "$sha_master" = "$sha_head"
+}
+
+
+test_expect_success 'setup a submodule tree' '
+       echo file > file &&
+       git add file &&
+       test_tick &&
+       git commit -m upstream
+       git clone . super &&
+       git clone super submodule &&
+       (cd super &&
+        git submodule add ../submodule submodule &&
+        test_tick &&
+        git commit -m "submodule" &&
+        git submodule init submodule
+       ) &&
+       (cd submodule &&
+       echo "line2" > file &&
+       git add file &&
+       git commit -m "Commit 2"
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         git pull --rebase origin
+        ) &&
+        git add submodule &&
+        git commit -m "submodule update"
+       )
+'
+
+test_expect_success 'submodule update detaching the HEAD ' '
+       (cd super/submodule &&
+        git reset --hard HEAD~1
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update submodule &&
+        cd submodule &&
+        ! compare_head
+       )
+'
+
+test_expect_success 'submodule update --rebase staying on master' '
+       (cd super/submodule &&
+         git checkout master
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update --rebase submodule &&
+        cd submodule &&
+        compare_head
+       )
+'
+
+test_expect_success 'submodule update - rebase in .git/config' '
+       (cd super &&
+        git config submodule.submodule.update rebase
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD~1
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update submodule &&
+        cd submodule &&
+        compare_head
+       )
+'
+
+test_expect_success 'submodule update - checkout in .git/config but --rebase given' '
+       (cd super &&
+        git config submodule.submodule.update checkout
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD~1
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update --rebase submodule &&
+        cd submodule &&
+        compare_head
+       )
+'
+
+test_expect_success 'submodule update - checkout in .git/config' '
+       (cd super &&
+        git config submodule.submodule.update checkout
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD^
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update submodule &&
+        cd submodule &&
+        ! compare_head
+       )
+'
+
+test_expect_success 'submodule init picks up rebase' '
+       (cd super &&
+        git config submodule.rebasing.url git://non-existing/git &&
+        git config submodule.rebasing.path does-not-matter &&
+        git config submodule.rebasing.update rebase &&
+        git submodule init rebasing &&
+        test "rebase" = $(git config submodule.rebasing.update)
+       )
+'
+
+test_done
index fcd5c266754a2b5de3dbbf94c8ef25aebff061f2..9cca14d080e63d25f5d71179deaced8b226bba07 100755 (executable)
@@ -4,7 +4,7 @@ test_description='git blame encoding conversion'
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/t8005/utf8.txt
-. "$TEST_DIRECTORY"/t8005/cp1251.txt
+. "$TEST_DIRECTORY"/t8005/iso8859-5.txt
 . "$TEST_DIRECTORY"/t8005/sjis.txt
 
 test_expect_success 'setup the repository' '
@@ -13,14 +13,14 @@ test_expect_success 'setup the repository' '
        git add file &&
        git commit --author "$UTF8_NAME <utf8@localhost>" -m "$UTF8_MSG" &&
 
-       echo "CP1251 LINE" >> file &&
+       echo "ISO-8859-5 LINE" >> file &&
        git add file &&
-       git config i18n.commitencoding cp1251 &&
-       git commit --author "$CP1251_NAME <cp1251@localhost>" -m "$CP1251_MSG" &&
+       git config i18n.commitencoding ISO8859-5 &&
+       git commit --author "$ISO8859_5_NAME <iso8859-5@localhost>" -m "$ISO8859_5_MSG" &&
 
        echo "SJIS LINE" >> file &&
        git add file &&
-       git config i18n.commitencoding shift-jis &&
+       git config i18n.commitencoding SJIS &&
        git commit --author "$SJIS_NAME <sjis@localhost>" -m "$SJIS_MSG"
 '
 
@@ -41,17 +41,17 @@ test_expect_success \
 '
 
 cat >expected <<EOF
-author $CP1251_NAME
-summary $CP1251_MSG
-author $CP1251_NAME
-summary $CP1251_MSG
-author $CP1251_NAME
-summary $CP1251_MSG
+author $ISO8859_5_NAME
+summary $ISO8859_5_MSG
+author $ISO8859_5_NAME
+summary $ISO8859_5_MSG
+author $ISO8859_5_NAME
+summary $ISO8859_5_MSG
 EOF
 
 test_expect_success \
        'blame respects i18n.logoutputencoding' '
-       git config i18n.logoutputencoding cp1251 &&
+       git config i18n.logoutputencoding ISO8859-5 &&
        git blame --incremental file | \
                egrep "^(author|summary) " > actual &&
        test_cmp actual expected
@@ -67,8 +67,8 @@ summary $UTF8_MSG
 EOF
 
 test_expect_success \
-       'blame respects --encoding=utf-8' '
-       git blame --incremental --encoding=utf-8 file | \
+       'blame respects --encoding=UTF-8' '
+       git blame --incremental --encoding=UTF-8 file | \
                egrep "^(author|summary) " > actual &&
        test_cmp actual expected
 '
@@ -76,8 +76,8 @@ test_expect_success \
 cat >expected <<EOF
 author $SJIS_NAME
 summary $SJIS_MSG
-author $CP1251_NAME
-summary $CP1251_MSG
+author $ISO8859_5_NAME
+summary $ISO8859_5_MSG
 author $UTF8_NAME
 summary $UTF8_MSG
 EOF
diff --git a/t/t8005/cp1251.txt b/t/t8005/cp1251.txt
deleted file mode 100644 (file)
index ce41e98..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-CP1251_NAME="Èâàí Ïåòðîâè÷ Ñèäîðîâ"
-CP1251_MSG="Òåñòîâîå ñîîáùåíèå"
diff --git a/t/t8005/iso8859-5.txt b/t/t8005/iso8859-5.txt
new file mode 100644 (file)
index 0000000..2e4b80c
--- /dev/null
@@ -0,0 +1,2 @@
+ISO8859_5_NAME="¸ÒÐÝ ¿ÕâàÞÒØç ÁØÔÞàÞÒ"
+ISO8859_5_MSG="ÂÕáâÞÒÞÕ áÞÞÑéÕÝØÕ"
index ce26ea4ac53a4608f6f8234b67868a9cc85be7f8..fb7d9f3e4a9e111e22299a29ca5af3a42101fbdc 100755 (executable)
@@ -148,6 +148,22 @@ test_expect_success 'Prompting works' '
                grep "^To: to@example.com$" msgtxt1
 '
 
+test_expect_success 'cccmd works' '
+       clean_fake_sendmail &&
+       cp $patches cccmd.patch &&
+       echo cccmd--cccmd@example.com >>cccmd.patch &&
+       echo sed -n s/^cccmd--//p \"\$1\" > cccmd-sed &&
+       chmod +x cccmd-sed &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --cc-cmd=./cccmd-sed \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               cccmd.patch \
+               &&
+       grep ^Cc:.*cccmd@example.com msgtxt1
+'
+
 z8=zzzzzzzz
 z64=$z8$z8$z8$z8$z8$z8$z8$z8
 z512=$z64$z64$z64$z64$z64$z64$z64$z64
@@ -274,7 +290,7 @@ EOF
 test_suppression () {
        git send-email \
                --dry-run \
-               --suppress-cc=$1 \
+               --suppress-cc=$1 ${2+"--suppress-cc=$2"} \
                --from="Example <from@example.com>" \
                --to=to@example.com \
                --smtp-server relay.example.com \
@@ -282,8 +298,8 @@ test_suppression () {
        sed     -e "s/^\(Date:\).*/\1 DATE-STRING/" \
                -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
                -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
-               >actual-suppress-$1 &&
-       test_cmp expected-suppress-$1 actual-suppress-$1
+               >actual-suppress-$1${2+"-$2"} &&
+       test_cmp expected-suppress-$1${2+"-$2"} actual-suppress-$1${2+"-$2"}
 }
 
 test_expect_success 'sendemail.cc set' '
@@ -316,6 +332,34 @@ test_expect_success 'sendemail.cc unset' '
        test_suppression sob
 '
 
+cat >expected-suppress-cccmd <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+(body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<committer@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>, One <one@example.com>, two@example.com, C O Mitter <committer@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success 'sendemail.cccmd' '
+       echo echo cc-cmd@example.com > cccmd &&
+       chmod +x cccmd &&
+       git config sendemail.cccmd ./cccmd &&
+       test_suppression cccmd
+'
+
 cat >expected-suppress-all <<\EOF
 0001-Second.patch
 Dry-OK. Log says:
@@ -341,13 +385,14 @@ cat >expected-suppress-body <<\EOF
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
 (mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
 (mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+(cc-cmd) Adding cc: cc-cmd@example.com from: './cccmd'
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<cc-cmd@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Cc: A <author@example.com>, One <one@example.com>, two@example.com, cc-cmd@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -360,6 +405,30 @@ test_expect_success '--suppress-cc=body' '
        test_suppression body
 '
 
+cat >expected-suppress-body-cccmd <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=body --suppress-cc=cccmd' '
+       test_suppression body cccmd
+'
+
 cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -381,6 +450,7 @@ Result: OK
 EOF
 
 test_expect_success '--suppress-cc=sob' '
+       git config --unset sendemail.cccmd
        test_suppression sob
 '
 
@@ -533,7 +603,7 @@ test_expect_success 'utf8 Cc is rfc2047 encoded' '
        --smtp-server="$(pwd)/fake.sendmail" \
        outdir/*.patch &&
        grep "^Cc:" msgtxt1 |
-       grep "=?utf-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>"
+       grep "=?UTF-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>"
 '
 
 test_expect_success '--compose adds MIME for utf8 body' '
@@ -550,7 +620,7 @@ test_expect_success '--compose adds MIME for utf8 body' '
          --smtp-server="$(pwd)/fake.sendmail" \
          $patches &&
        grep "^utf8 body" msgtxt1 &&
-       grep "^Content-Type: text/plain; charset=utf-8" msgtxt1
+       grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1
 '
 
 test_expect_success '--compose respects user mime type' '
@@ -573,7 +643,7 @@ test_expect_success '--compose respects user mime type' '
          $patches &&
        grep "^utf8 body" msgtxt1 &&
        grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 &&
-       ! grep "^Content-Type: text/plain; charset=utf-8" msgtxt1
+       ! grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1
 '
 
 test_expect_success '--compose adds MIME for utf8 subject' '
@@ -586,7 +656,7 @@ test_expect_success '--compose adds MIME for utf8 subject' '
          --smtp-server="$(pwd)/fake.sendmail" \
          $patches &&
        grep "^fake edit" msgtxt1 &&
-       grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
+       grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
 test_expect_success 'detects ambiguous reference/file conflict' '
@@ -621,4 +691,25 @@ test_expect_success 'in-reply-to but no threading' '
        grep "In-Reply-To: <in-reply-id@example.com>"
 '
 
+test_expect_success 'no in-reply-to and no threading' '
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --nothread \
+               $patches $patches >stdout &&
+       ! grep "In-Reply-To: " stdout
+'
+
+test_expect_success 'threading but no chain-reply-to' '
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --thread \
+               --nochain-reply-to \
+               $patches $patches >stdout &&
+       grep "In-Reply-To: " stdout
+'
+
 test_done
index 4eee2e9fa6bacdc0d130a692f727885c5fa42e49..64aa7e2f104eba5f9f844c40d520163a8463cc8f 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success \
        echo "zzz" > bar/zzz &&
        echo "#!/bin/sh" > exec.sh &&
        chmod +x exec.sh &&
-       svn import -m "import for git svn" . "$svnrepo" >/dev/null &&
+       svn_cmd import -m "import for git svn" . "$svnrepo" >/dev/null &&
        cd .. &&
        rm -rf import &&
        git svn init "$svnrepo"'
@@ -51,7 +51,7 @@ test_expect_success "$name" '
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
                ${remotes_git_svn}..mybranch &&
-       svn up "$SVN_TREE" &&
+       svn_cmd up "$SVN_TREE" &&
        test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a'
 
 
@@ -118,7 +118,7 @@ test_expect_success "$name" '
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
                ${remotes_git_svn}..mybranch5 &&
-       svn up "$SVN_TREE" &&
+       svn_cmd up "$SVN_TREE" &&
        test ! -x "$SVN_TREE"/exec.sh'
 
 
@@ -129,7 +129,7 @@ test_expect_success "$name" '
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
                ${remotes_git_svn}..mybranch5 &&
-       svn up "$SVN_TREE" &&
+       svn_cmd up "$SVN_TREE" &&
        test -x "$SVN_TREE"/exec.sh'
 
 
@@ -141,7 +141,7 @@ test_expect_success "$name" '
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
                ${remotes_git_svn}..mybranch5 &&
-       svn up "$SVN_TREE" &&
+       svn_cmd up "$SVN_TREE" &&
        test -L "$SVN_TREE"/exec.sh'
 
 name='new symlink is added to a file that was also just made executable'
@@ -153,7 +153,7 @@ test_expect_success "$name" '
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
                ${remotes_git_svn}..mybranch5 &&
-       svn up "$SVN_TREE" &&
+       svn_cmd up "$SVN_TREE" &&
        test -x "$SVN_TREE"/bar/zzz &&
        test -L "$SVN_TREE"/exec-2.sh'
 
@@ -166,7 +166,7 @@ test_expect_success "$name" '
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
                ${remotes_git_svn}..mybranch5 &&
-       svn up "$SVN_TREE" &&
+       svn_cmd up "$SVN_TREE" &&
        test -f "$SVN_TREE"/exec-2.sh &&
        test ! -L "$SVN_TREE"/exec-2.sh &&
        test_cmp help "$SVN_TREE"/exec-2.sh'
index 1e31d6ea7253ee4216347fd707d1408f97af32fa..9da4178c94f4bde62f69f32701b16ab3d04524e0 100755 (executable)
@@ -48,7 +48,7 @@ EOF
        printf "\r\n" > empty_crlf
        a_empty_crlf=`git hash-object -w empty_crlf`
 
-       svn import --no-auto-props -m 'import for git svn' . "$svnrepo" >/dev/null
+       svn_cmd import --no-auto-props -m 'import for git svn' . "$svnrepo" >/dev/null
 cd ..
 
 rm -rf import
@@ -57,13 +57,13 @@ test_expect_success 'setup some commits to svn' \
        'cd test_wc &&
                echo Greetings >> kw.c &&
                poke kw.c &&
-               svn commit -m "Not yet an Id" &&
+               svn_cmd commit -m "Not yet an Id" &&
                echo Hello world >> kw.c &&
                poke kw.c &&
-               svn commit -m "Modified file, but still not yet an Id" &&
-               svn propset svn:keywords Id kw.c &&
+               svn_cmd commit -m "Modified file, but still not yet an Id" &&
+               svn_cmd propset svn:keywords Id kw.c &&
                poke kw.c &&
-               svn commit -m "Propset Id" &&
+               svn_cmd commit -m "Propset Id" &&
        cd ..'
 
 test_expect_success 'initialize git svn' 'git svn init "$svnrepo"'
@@ -83,16 +83,16 @@ test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'"
 
 test_expect_success "propset CR on crlf files" \
        'cd test_wc &&
-               svn propset svn:eol-style CR empty &&
-               svn propset svn:eol-style CR crlf &&
-               svn propset svn:eol-style CR ne_crlf &&
-               svn commit -m "propset CR on crlf files" &&
+               svn_cmd propset svn:eol-style CR empty &&
+               svn_cmd propset svn:eol-style CR crlf &&
+               svn_cmd propset svn:eol-style CR ne_crlf &&
+               svn_cmd commit -m "propset CR on crlf files" &&
         cd ..'
 
 test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
        'git svn fetch &&
         git pull . ${remotes_git_svn} &&
-        svn co "$svnrepo" new_wc'
+        svn_cmd co "$svnrepo" new_wc'
 
 for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
 do
@@ -106,11 +106,11 @@ cd test_wc
        a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git hash-object --stdin`
        a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git hash-object --stdin`
        test_expect_success 'Set CRLF on cr files' \
-       'svn propset svn:eol-style CRLF cr &&
-        svn propset svn:eol-style CRLF ne_cr &&
-        svn propset svn:keywords Id cr &&
-        svn propset svn:keywords Id ne_cr &&
-        svn commit -m "propset CRLF on cr files"'
+       'svn_cmd propset svn:eol-style CRLF cr &&
+        svn_cmd propset svn:eol-style CRLF ne_cr &&
+        svn_cmd propset svn:keywords Id cr &&
+        svn_cmd propset svn:keywords Id ne_cr &&
+        svn_cmd commit -m "propset CRLF on cr files"'
 cd ..
 test_expect_success 'fetch and pull latest from svn' \
        'git svn fetch && git pull . ${remotes_git_svn}'
@@ -140,10 +140,10 @@ test_expect_success 'test show-ignore' "
        cd test_wc &&
        mkdir -p deeply/nested/directory &&
        touch deeply/nested/directory/.keep &&
-       svn add deeply &&
-       svn up &&
-       svn propset -R svn:ignore 'no-such-file*' .
-       svn commit -m 'propset svn:ignore'
+       svn_cmd add deeply &&
+       svn_cmd up &&
+       svn_cmd propset -R svn:ignore 'no-such-file*' .
+       svn_cmd commit -m 'propset svn:ignore'
        cd .. &&
        git svn show-ignore > show-ignore.got &&
        cmp show-ignore.expect show-ignore.got
index e2232180158cfb0e523c8ffdd3ac10bf61c8f4ee..028fb19e09bbb9e2720e95a5e0b4ddb221f4a0e1 100755 (executable)
@@ -9,7 +9,7 @@ test_expect_success 'initialize repo' '
        mkdir -p deeply/nested/directory/number/2 &&
        echo foo > deeply/nested/directory/number/1/file &&
        echo foo > deeply/nested/directory/number/2/another &&
-       svn import -m "import for git svn" . "$svnrepo" &&
+       svn_cmd import -m "import for git svn" . "$svnrepo" &&
        cd ..
        '
 
@@ -23,7 +23,7 @@ test_expect_success 'Try a commit on rmdir' '
        git rm -f deeply/nested/directory/number/2/another &&
        git commit -a -m "remove another" &&
        git svn set-tree --rmdir HEAD &&
-       svn ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1
+       svn_cmd ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1
        '
 
 
index 963dd95e4a71ccefa64b9500c490f44ea6d7c789..3413164cb127da9e8d601775769b3b049816173e 100755 (executable)
@@ -10,15 +10,15 @@ test_expect_success 'make history for tracking' '
        mkdir import &&
        mkdir import/trunk &&
        echo hello >> import/trunk/README &&
-       svn import -m initial import "$svnrepo" &&
+       svn_cmd import -m initial import "$svnrepo" &&
        rm -rf import &&
-       svn co "$svnrepo"/trunk trunk &&
+       svn_cmd co "$svnrepo"/trunk trunk &&
        echo bye bye >> trunk/README &&
-       svn rm -m "gone" "$svnrepo"/trunk &&
+       svn_cmd rm -m "gone" "$svnrepo"/trunk &&
        rm -rf trunk &&
        mkdir trunk &&
        echo "new" > trunk/FOLLOWME &&
-       svn import -m "new trunk" trunk "$svnrepo"/trunk
+       svn_cmd import -m "new trunk" trunk "$svnrepo"/trunk
 '
 
 test_expect_success 'clone repo with git' '
index ab9fa322200b333dd0222be6b712c6651f4419fb..78610b61e63596dad7e86a19a7e39deda3b19222 100755 (executable)
@@ -11,18 +11,18 @@ test_expect_success 'initialize repo' '
        cd import &&
        mkdir -p trunk &&
        echo hello > trunk/readme &&
-       svn import -m "initial" . "$svnrepo" &&
+       svn_cmd import -m "initial" . "$svnrepo" &&
        cd .. &&
-       svn co "$svnrepo" wc &&
+       svn_cmd co "$svnrepo" wc &&
        cd wc &&
        echo world >> trunk/readme &&
        poke trunk/readme &&
-       svn commit -m "another commit" &&
-       svn up &&
-       svn mv trunk thunk &&
+       svn_cmd commit -m "another commit" &&
+       svn_cmd up &&
+       svn_cmd mv trunk thunk &&
        echo goodbye >> thunk/readme &&
        poke thunk/readme &&
-       svn commit -m "bye now" &&
+       svn_cmd commit -m "bye now" &&
        cd ..
        '
 
@@ -51,7 +51,7 @@ test_expect_success 'init and fetch from one svn-remote' '
         '
 
 test_expect_success 'follow deleted parent' '
-        (svn cp -m "resurrecting trunk as junk" \
+        (svn_cmd cp -m "resurrecting trunk as junk" \
                "$svnrepo"/trunk@2 "$svnrepo"/junk ||
          svn cp -m "resurrecting trunk as junk" \
                -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
@@ -97,8 +97,8 @@ test_expect_success 'follow higher-level parent' '
         '
 
 test_expect_success 'follow deleted directory' '
-       svn mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye &&
-       svn rm -m "remove glob" "$svnrepo"/glob &&
+       svn_cmd mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye &&
+       svn_cmd rm -m "remove glob" "$svnrepo"/glob &&
        git svn init --minimize-url -i glob "$svnrepo"/glob &&
        git svn fetch -i glob &&
        test "`git cat-file blob refs/remotes/glob:blob/bye`" = hi &&
@@ -120,7 +120,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' '
        cd import &&
          svn import -m "r9270 test" . "$svnrepo"/r9270 &&
        cd .. &&
-       svn co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 &&
+       svn_cmd co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 &&
        cd r9270 &&
          svn mkdir native &&
          svn mv t native/t &&
@@ -138,7 +138,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' '
        '
 
 test_expect_success "track initial change if it was only made to parent" '
-       svn cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk &&
+       svn_cmd cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk &&
        git svn init --minimize-url -i r9270-d \
          "$svnrepo"/r9270/drunk/subversion/bindings/swig/perl/native/t &&
        git svn fetch -i r9270-d &&
@@ -152,20 +152,20 @@ test_expect_success "track initial change if it was only made to parent" '
 test_expect_success "follow-parent is atomic" '
        (
                cd wc &&
-               svn up &&
-               svn mkdir stunk &&
+               svn_cmd up &&
+               svn_cmd mkdir stunk &&
                echo "trunk stunk" > stunk/readme &&
-               svn add stunk/readme &&
-               svn ci -m "trunk stunk" &&
+               svn_cmd add stunk/readme &&
+               svn_cmd ci -m "trunk stunk" &&
                echo "stunk like junk" >> stunk/readme &&
-               svn ci -m "really stunk" &&
+               svn_cmd ci -m "really stunk" &&
                echo "stink stank stunk" >> stunk/readme &&
-               svn ci -m "even the grinch agrees"
+               svn_cmd ci -m "even the grinch agrees"
        ) &&
-       svn copy -m "stunk flunked" "$svnrepo"/stunk "$svnrepo"/flunk &&
+       svn_cmd copy -m "stunk flunked" "$svnrepo"/stunk "$svnrepo"/flunk &&
        { svn cp -m "early stunk flunked too" \
                "$svnrepo"/stunk@17 "$svnrepo"/flunked ||
-       svn cp -m "early stunk flunked too" \
+       svn_cmd cp -m "early stunk flunked too" \
                -r17 "$svnrepo"/stunk "$svnrepo"/flunked; } &&
        git svn init --minimize-url -i stunk "$svnrepo"/stunk &&
        git svn fetch -i stunk &&
@@ -192,7 +192,7 @@ test_expect_success "follow-parent is atomic" '
        '
 
 test_expect_success "track multi-parent paths" '
-       svn cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob &&
+       svn_cmd cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob &&
        git svn multi-fetch &&
        test `git cat-file commit refs/remotes/glob | \
               grep "^parent " | wc -l` -eq 2
index ba99abb6d975351cf2725e757a588a26109a9723..dd48e9cba832a06a34fd1e2c895bb1b19792168f 100755 (executable)
@@ -8,7 +8,7 @@ test_expect_success 'initialize repo' '
        mkdir import &&
        cd import &&
        echo hello > readme &&
-       svn import -m "initial" . "$svnrepo" &&
+       svn_cmd import -m "initial" . "$svnrepo" &&
        cd .. &&
        echo hello > readme &&
        git update-index --add readme &&
@@ -27,16 +27,16 @@ prev=`git rev-parse --verify HEAD^1`
 test_expect_success 'test the commit-diff command' '
        test -n "$prev" && test -n "$head" &&
        git svn commit-diff -r1 "$prev" "$head" "$svnrepo" &&
-       svn co "$svnrepo" wc &&
+       svn_cmd co "$svnrepo" wc &&
        cmp readme wc/readme
        '
 
 test_expect_success 'commit-diff to a sub-directory (with git svn config)' '
-       svn import -m "sub-directory" import "$svnrepo"/subdir &&
+       svn_cmd import -m "sub-directory" import "$svnrepo"/subdir &&
        git svn init --minimize-url "$svnrepo"/subdir &&
        git svn fetch &&
        git svn commit-diff -r3 "$prev" "$head" &&
-       svn cat "$svnrepo"/subdir/readme > readme.2 &&
+       svn_cmd cat "$svnrepo"/subdir/readme > readme.2 &&
        cmp readme readme.2
        '
 
index 6eb0fd85c86ec194062af7da8c7002f0819bcc07..12f21b700ec5216b6aeaa886f862d3e3b1f6682f 100755 (executable)
@@ -8,18 +8,18 @@ test_expect_success 'initialize repo' '
        mkdir import &&
        cd import &&
        echo initial > file &&
-       svn import -m "initial" . "$svnrepo" &&
+       svn_cmd import -m "initial" . "$svnrepo" &&
        cd .. &&
        echo initial > file &&
        git update-index --add file &&
        git commit -a -m "initial"
        '
 test_expect_success 'commit change from svn side' '
-       svn co "$svnrepo" t.svn &&
+       svn_cmd co "$svnrepo" t.svn &&
        cd t.svn &&
        echo second line from svn >> file &&
        poke file &&
-       svn commit -m "second line from svn" &&
+       svn_cmd commit -m "second line from svn" &&
        cd .. &&
        rm -rf t.svn
        '
@@ -43,11 +43,11 @@ test_expect_success 'dcommit fails to commit because of conflict' '
        git svn init "$svnrepo" &&
        git svn fetch &&
        git reset --hard refs/${remotes_git_svn} &&
-       svn co "$svnrepo" t.svn &&
+       svn_cmd co "$svnrepo" t.svn &&
        cd t.svn &&
        echo fourth line from svn >> file &&
        poke file &&
-       svn commit -m "fourth line from svn" &&
+       svn_cmd commit -m "fourth line from svn" &&
        cd .. &&
        rm -rf t.svn &&
        echo "fourth line from git" >> file &&
@@ -67,11 +67,11 @@ test_expect_success 'dcommit does the svn equivalent of an index merge' "
        "
 
 test_expect_success 'commit another change from svn side' '
-       svn co "$svnrepo" t.svn &&
+       svn_cmd co "$svnrepo" t.svn &&
        cd t.svn &&
                echo third line from svn >> file &&
                poke file &&
-               svn commit -m "third line from svn" &&
+               svn_cmd commit -m "third line from svn" &&
        cd .. &&
        rm -rf t.svn
        '
index acad16a6f0f9b3b45b4766474e15ee5019ec2ce2..3a9e07768d8e6bebd7a4c4156ace6c5746d7d440 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success 'setup old-looking metadata' '
                        mkdir -p $i && \
                        echo hello >> $i/README || exit 1
                done && \
-               svn import -m test . "$svnrepo"
+               svn_cmd import -m test . "$svnrepo"
                cd .. &&
        git svn init "$svnrepo" &&
        git svn fetch &&
index d8582b1aa5d178778623bfb4386a66f58e165c17..d732d3130299e964359784949fc97805a2888e39 100755 (executable)
@@ -14,30 +14,30 @@ test_expect_success 'test refspec globbing' '
        mkdir -p trunk/src/a trunk/src/b trunk/doc &&
        echo "hello world" > trunk/src/a/readme &&
        echo "goodbye world" > trunk/src/b/readme &&
-       svn import -m "initial" trunk "$svnrepo"/trunk &&
-       svn co "$svnrepo" tmp &&
+       svn_cmd import -m "initial" trunk "$svnrepo"/trunk &&
+       svn_cmd co "$svnrepo" tmp &&
        (
                cd tmp &&
                mkdir branches tags &&
-               svn add branches tags &&
-               svn cp trunk branches/start &&
-               svn commit -m "start a new branch" &&
-               svn up &&
+               svn_cmd add branches tags &&
+               svn_cmd cp trunk branches/start &&
+               svn_cmd commit -m "start a new branch" &&
+               svn_cmd up &&
                echo "hi" >> branches/start/src/b/readme &&
                poke branches/start/src/b/readme &&
                echo "hey" >> branches/start/src/a/readme &&
                poke branches/start/src/a/readme &&
-               svn commit -m "hi" &&
-               svn up &&
-               svn cp branches/start tags/end &&
+               svn_cmd commit -m "hi" &&
+               svn_cmd up &&
+               svn_cmd cp branches/start tags/end &&
                echo "bye" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
                echo "aye" >> tags/end/src/a/readme &&
                poke tags/end/src/a/readme &&
-               svn commit -m "the end" &&
+               svn_cmd commit -m "the end" &&
                echo "byebye" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               svn commit -m "nothing to see here"
+               svn_cmd commit -m "nothing to see here"
        ) &&
        git config --add svn-remote.svn.url "$svnrepo" &&
        git config --add svn-remote.svn.fetch \
@@ -72,7 +72,7 @@ test_expect_success 'test left-hand-side only globbing' '
                cd tmp &&
                echo "try try" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               svn commit -m "try to try"
+               svn_cmd commit -m "try to try"
        ) &&
        git svn fetch two &&
        test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
@@ -102,7 +102,7 @@ test_expect_success 'test disallow multi-globs' '
                cd tmp &&
                echo "try try" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               svn commit -m "try to try"
+               svn_cmd commit -m "try to try"
        ) &&
        test_must_fail git svn fetch three 2> stderr.three &&
        test_cmp expect.three stderr.three
index 8f79c3f251aed240e0ba59244a8cfd91db2369d2..c318f9f946a35f0d13b90f67dd00a6817d77d989 100755 (executable)
@@ -14,30 +14,30 @@ test_expect_success 'test refspec globbing' '
        mkdir -p trunk/src/a trunk/src/b trunk/doc &&
        echo "hello world" > trunk/src/a/readme &&
        echo "goodbye world" > trunk/src/b/readme &&
-       svn import -m "initial" trunk "$svnrepo"/trunk &&
-       svn co "$svnrepo" tmp &&
+       svn_cmd import -m "initial" trunk "$svnrepo"/trunk &&
+       svn_cmd co "$svnrepo" tmp &&
        (
                cd tmp &&
                mkdir branches branches/v1 tags &&
-               svn add branches tags &&
-               svn cp trunk branches/v1/start &&
-               svn commit -m "start a new branch" &&
-               svn up &&
+               svn_cmd add branches tags &&
+               svn_cmd cp trunk branches/v1/start &&
+               svn_cmd commit -m "start a new branch" &&
+               svn_cmd up &&
                echo "hi" >> branches/v1/start/src/b/readme &&
                poke branches/v1/start/src/b/readme &&
                echo "hey" >> branches/v1/start/src/a/readme &&
                poke branches/v1/start/src/a/readme &&
-               svn commit -m "hi" &&
-               svn up &&
-               svn cp branches/v1/start tags/end &&
+               svn_cmd commit -m "hi" &&
+               svn_cmd up &&
+               svn_cmd cp branches/v1/start tags/end &&
                echo "bye" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
                echo "aye" >> tags/end/src/a/readme &&
                poke tags/end/src/a/readme &&
-               svn commit -m "the end" &&
+               svn_cmd commit -m "the end" &&
                echo "byebye" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               svn commit -m "nothing to see here"
+               svn_cmd commit -m "nothing to see here"
        ) &&
        git config --add svn-remote.svn.url "$svnrepo" &&
        git config --add svn-remote.svn.fetch \
@@ -72,7 +72,7 @@ test_expect_success 'test left-hand-side only globbing' '
                cd tmp &&
                echo "try try" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               svn commit -m "try to try"
+               svn_cmd commit -m "try to try"
        ) &&
        git svn fetch two &&
        test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
@@ -97,25 +97,25 @@ test_expect_success 'test another branch' '
        (
                cd tmp &&
                mkdir branches/v2 &&
-               svn add branches/v2 &&
-               svn cp trunk branches/v2/start &&
-               svn commit -m "Another versioned branch" &&
-               svn up &&
+               svn_cmd add branches/v2 &&
+               svn_cmd cp trunk branches/v2/start &&
+               svn_cmd commit -m "Another versioned branch" &&
+               svn_cmd up &&
                echo "hello" >> branches/v2/start/src/b/readme &&
                poke branches/v2/start/src/b/readme &&
                echo "howdy" >> branches/v2/start/src/a/readme &&
                poke branches/v2/start/src/a/readme &&
-               svn commit -m "Changed 2 in v2/start" &&
-               svn up &&
-               svn cp branches/v2/start tags/next &&
+               svn_cmd commit -m "Changed 2 in v2/start" &&
+               svn_cmd up &&
+               svn_cmd cp branches/v2/start tags/next &&
                echo "bye" >> tags/next/src/b/readme &&
                poke tags/next/src/b/readme &&
                echo "aye" >> tags/next/src/a/readme &&
                poke tags/next/src/a/readme &&
-               svn commit -m "adding more" &&
+               svn_cmd commit -m "adding more" &&
                echo "byebye" >> tags/next/src/b/readme &&
                poke tags/next/src/b/readme &&
-               svn commit -m "adios"
+               svn_cmd commit -m "adios"
        ) &&
        git config --add svn-remote.four.url "$svnrepo" &&
        git config --add svn-remote.four.fetch trunk:refs/remotes/four/trunk &&
@@ -151,7 +151,7 @@ test_expect_success 'test disallow multiple globs' '
                cd tmp &&
                echo "try try" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               svn commit -m "try to try"
+               svn_cmd commit -m "try to try"
        ) &&
        test_must_fail git svn fetch three 2> stderr.three &&
        test_cmp expect.three stderr.three
index e9b6128b3fa5c3d0cbbe5abb7f3e55267263449d..e8479cec7abade29aec91b22bba69d4ab9804f21 100755 (executable)
@@ -15,7 +15,7 @@ test_description='git svn dcommit new files over svn:// test'
 require_svnserve
 
 test_expect_success 'start tracking an empty repo' '
-       svn mkdir -m "empty dir" "$svnrepo"/empty-dir &&
+       svn_cmd mkdir -m "empty dir" "$svnrepo"/empty-dir &&
        echo "[general]" > "$rawsvnrepo"/conf/svnserve.conf &&
        echo anon-access = write >> "$rawsvnrepo"/conf/svnserve.conf &&
        start_svnserve &&
index 17b2855c4f308d463ea239f355549120dab4f80f..84f7c9b4bb00931b57740be32ce7ee29bbb26694 100755 (executable)
@@ -35,12 +35,12 @@ EOF
 }
 
 test_expect_success 'setup svn repository' '
-       svn co "$svnrepo" mysvnwork &&
+       svn_cmd co "$svnrepo" mysvnwork &&
        mkdir -p mysvnwork/trunk &&
        cd mysvnwork &&
                big_text_block >> trunk/README &&
-               svn add trunk &&
-               svn ci -m "first commit" trunk &&
+               svn_cmd add trunk &&
+               svn_cmd ci -m "first commit" trunk &&
                cd ..
        '
 
index fd6d1d20463f2a4de1bd8ed739c0e977921e037c..0374a7476bcc5b254aa1464d468434ae3205f114 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success 'setup repository and import' '
                        mkdir -p $i && \
                        echo hello >> $i/README || exit 1
                done && \
-               svn import -m test . "$svnrepo"
+               svn_cmd import -m test . "$svnrepo"
                cd .. &&
        git svn init "$svnrepo" -T trunk -b branches -t tags &&
        git svn fetch &&
index dde46cd92fba24221393b15233ed248997161caf..b7ef9e25895550e8749952e2ed54d7827737242e 100755 (executable)
@@ -16,7 +16,7 @@ cd tmp
 test_expect_success 'setup svnrepo' '
        mkdir project project/trunk project/branches project/tags &&
        echo foo > project/trunk/foo &&
-       svn import -m "$test_description" project "$svnrepo"/project &&
+       svn_cmd import -m "$test_description" project "$svnrepo"/project &&
        rm -rf project
        '
 
index 7a7c12868758d6e3bd9d1f8ec4fe32c0663115f4..ac52bff0ef540bce48e4ee02a53adbbec3662939 100755 (executable)
@@ -13,13 +13,13 @@ scary_ref='Abo-Uebernahme%20(Bug%20#994)'
 test_expect_success 'setup svnrepo' '
        mkdir project project/trunk project/branches project/tags &&
        echo foo > project/trunk/foo &&
-       svn import -m "$test_description" project "$svnrepo/pr ject" &&
+       svn_cmd import -m "$test_description" project "$svnrepo/pr ject" &&
        rm -rf project &&
-       svn cp -m "fun" "$svnrepo/pr ject/trunk" \
+       svn_cmd cp -m "fun" "$svnrepo/pr ject/trunk" \
                        "$svnrepo/pr ject/branches/fun plugin" &&
-       svn cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \
+       svn_cmd cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \
                              "$svnrepo/pr ject/branches/more fun plugin!" &&
-       svn cp -m "scary" "$svnrepo/pr ject/branches/fun plugin" \
+       svn_cmd cp -m "scary" "$svnrepo/pr ject/branches/fun plugin" \
                      "$svnrepo/pr ject/branches/$scary_uri" &&
        start_httpd
        '
index 27dd7c273a4a1ae77a8e48b2e4b4ce1a3ae19a56..95741cbbac6bf2e59531bbe1f9527ce4596fa904 100755 (executable)
@@ -7,7 +7,7 @@ test_description='git svn info'
 . ./lib-git-svn.sh
 
 # Tested with: svn, version 1.4.4 (r25188)
-v=`svn --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p'`
+v=`svn_cmd --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p'`
 case $v in
 1.[45].*)
        ;;
@@ -31,7 +31,7 @@ ptouch() {
                        my $atime = $mtime;
                        utime $atime, $mtime, $git_file;
                }
-       ' "`svn info $2 | grep '^Text Last Updated:'`" "$1"
+       ' "`svn_cmd info $2 | grep '^Text Last Updated:'`" "$1"
 }
 
 quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
@@ -45,14 +45,14 @@ test_expect_success 'setup repository and import' '
                mkdir directory &&
                touch directory/.placeholder &&
                ln -s directory symlink-directory &&
-               svn import -m "initial" . "$svnrepo" &&
+               svn_cmd import -m "initial" . "$svnrepo" &&
        cd .. &&
-       svn co "$svnrepo" svnwc &&
+       svn_cmd co "$svnrepo" svnwc &&
        cd svnwc &&
                echo foo > foo &&
-               svn add foo &&
-               svn commit -m "change outside directory" &&
-               svn update &&
+               svn_cmd add foo &&
+               svn_cmd commit -m "change outside directory" &&
+               svn_cmd update &&
        cd .. &&
        mkdir gitwc &&
        cd gitwc &&
@@ -143,7 +143,7 @@ test_expect_success 'info added-file' "
        cp gitwc/added-file svnwc/added-file &&
        ptouch gitwc/added-file svnwc/added-file &&
        cd svnwc &&
-               svn add added-file > /dev/null &&
+               svn_cmd add added-file > /dev/null &&
        cd .. &&
        (cd svnwc; svn info added-file) > expected.info-added-file &&
        (cd gitwc; git svn info added-file) > actual.info-added-file &&
@@ -160,7 +160,7 @@ test_expect_success 'info added-directory' "
        ptouch gitwc/added-directory svnwc/added-directory &&
        touch gitwc/added-directory/.placeholder &&
        cd svnwc &&
-               svn add added-directory > /dev/null &&
+               svn_cmd add added-directory > /dev/null &&
        cd .. &&
        cd gitwc &&
                git add added-directory &&
@@ -184,7 +184,7 @@ test_expect_success 'info added-symlink-file' "
        cd .. &&
        cd svnwc &&
                ln -s added-file added-symlink-file &&
-               svn add added-symlink-file > /dev/null &&
+               svn_cmd add added-symlink-file > /dev/null &&
        cd .. &&
        ptouch gitwc/added-symlink-file svnwc/added-symlink-file &&
        (cd svnwc; svn info added-symlink-file) \
@@ -207,7 +207,7 @@ test_expect_success 'info added-symlink-directory' "
        cd .. &&
        cd svnwc &&
                ln -s added-directory added-symlink-directory &&
-               svn add added-symlink-directory > /dev/null &&
+               svn_cmd add added-symlink-directory > /dev/null &&
        cd .. &&
        ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory &&
        (cd svnwc; svn info added-symlink-directory) \
@@ -233,7 +233,7 @@ test_expect_success 'info deleted-file' "
                git rm -f file > /dev/null &&
        cd .. &&
        cd svnwc &&
-               svn rm --force file > /dev/null &&
+               svn_cmd rm --force file > /dev/null &&
        cd .. &&
        (cd svnwc; svn info file) |
        sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
@@ -254,7 +254,7 @@ test_expect_success 'info deleted-directory' "
                git rm -r -f directory > /dev/null &&
        cd .. &&
        cd svnwc &&
-               svn rm --force directory > /dev/null &&
+               svn_cmd rm --force directory > /dev/null &&
        cd .. &&
        (cd svnwc; svn info directory) |
        sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
@@ -275,7 +275,7 @@ test_expect_success 'info deleted-symlink-file' "
                git rm -f symlink-file > /dev/null &&
        cd .. &&
        cd svnwc &&
-               svn rm --force symlink-file > /dev/null &&
+               svn_cmd rm --force symlink-file > /dev/null &&
        cd .. &&
        (cd svnwc; svn info symlink-file) |
        sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
@@ -297,7 +297,7 @@ test_expect_success 'info deleted-symlink-directory' "
                git rm -f symlink-directory > /dev/null &&
        cd .. &&
        cd svnwc &&
-               svn rm --force symlink-directory > /dev/null &&
+               svn_cmd rm --force symlink-directory > /dev/null &&
        cd .. &&
        (cd svnwc; svn info symlink-directory) |
        sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
index ef2c0523cd4566b3d1c4925ba608efc16524546a..f159ab689bd5f7dfba1b27adb9dc68d8b65b67fe 100755 (executable)
@@ -9,22 +9,17 @@ test_description='git svn clone with percent escapes'
 test_expect_success 'setup svnrepo' '
        mkdir project project/trunk project/branches project/tags &&
        echo foo > project/trunk/foo &&
-       svn import -m "$test_description" project "$svnrepo/pr ject" &&
+       svn_cmd import -m "$test_description" project "$svnrepo/pr ject" &&
        rm -rf project &&
        start_httpd
 '
 
-if test "$SVN_HTTPD_PORT" = ""
-then
-       test_expect_failure 'test clone with percent escapes - needs SVN_HTTPD_PORT set' 'false'
-else
-       test_expect_success 'test clone with percent escapes' '
-               git svn clone "$svnrepo/pr%20ject" clone &&
-               cd clone &&
-                       git rev-parse refs/${remotes_git_svn} &&
-               cd ..
-       '
-fi
+test_expect_success 'test clone with percent escapes' '
+       git svn clone "$svnrepo/pr%20ject" clone &&
+       cd clone &&
+               git rev-parse refs/${remotes_git_svn} &&
+       cd ..
+'
 
 stop_httpd
 
index 1b1cf47281d10c64ac57e96701c727f19f189948..30013b7bb948640869d5c5208b2feeb08c011b73 100755 (executable)
@@ -4,12 +4,12 @@ test_description='git svn authorship'
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repository' '
-       svn checkout "$svnrepo" work.svn &&
+       svn_cmd checkout "$svnrepo" work.svn &&
        (
                cd work.svn &&
                echo >file
-               svn add file
-               svn commit -m "first commit" file
+               svn_cmd add file
+               svn_cmd commit -m "first commit" file
        )
 '
 
@@ -74,10 +74,10 @@ test_expect_success 'interact with it via git svn' '
        # Make sure there are no svn commit messages with excess blank lines
        (
                cd work.svn &&
-               svn up &&
+               svn_cmd up &&
                
-               test $(svn log -r2:2 | wc -l) = 5 &&
-               test $(svn log -r4:4 | wc -l) = 7
+               test $(svn_cmd log -r2:2 | wc -l) = 5 &&
+               test $(svn_cmd log -r4:4 | wc -l) = 7
        )
 '
 
index cf0415274c2d2abae7a6850818f4de8063cb61a7..045521615c64d1ae938524b0424e1e4260ca8c54 100755 (executable)
@@ -10,7 +10,7 @@ test_description='git svn respects rewriteRoot during rebuild'
 mkdir import
 cd import
        touch foo
-       svn import -m 'import for git svn' . "$svnrepo" >/dev/null
+       svn_cmd import -m 'import for git svn' . "$svnrepo" >/dev/null
 cd ..
 rm -rf import
 
index 263dbf5fc276ffa052096810f59565a851245b7f..d6b076f6b7de148c8d548c7a977e4f09b4be8e3b 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success 'initialize git svn' '
        (
                cd import &&
                echo foo >foo &&
-               svn import -m "import for git svn" . "$svnrepo"
+               svn_cmd import -m "import for git svn" . "$svnrepo"
        ) &&
        rm -rf import &&
        git svn init "$svnrepo"
@@ -61,23 +61,23 @@ test_expect_success 'check resulting svn repository' '
 (
        mkdir work &&
        cd work &&
-       svn co "$svnrepo" &&
+       svn_cmd co "$svnrepo" &&
        cd svnrepo &&
 
        # Check properties from first commit.
-       test "x$(svn propget svn:executable exec1.sh)" = "x*" &&
-       test "x$(svn propget svn:mime-type exec1.sh)" = \
+       test "x$(svn_cmd propget svn:executable exec1.sh)" = "x*" &&
+       test "x$(svn_cmd propget svn:mime-type exec1.sh)" = \
             "xapplication/x-shellscript" &&
-       test "x$(svn propget svn:mime-type hello.txt)" = "xtext/plain" &&
-       test "x$(svn propget svn:eol-style hello.txt)" = "xnative" &&
-       test "x$(svn propget svn:mime-type bar)" = "x" &&
+       test "x$(svn_cmd propget svn:mime-type hello.txt)" = "xtext/plain" &&
+       test "x$(svn_cmd propget svn:eol-style hello.txt)" = "xnative" &&
+       test "x$(svn_cmd propget svn:mime-type bar)" = "x" &&
 
        # Check properties from second commit.
-       test "x$(svn propget svn:executable exec2.sh)" = "x*" &&
-       test "x$(svn propget svn:mime-type exec2.sh)" = "x" &&
-       test "x$(svn propget svn:mime-type world.txt)" = "x" &&
-       test "x$(svn propget svn:eol-style world.txt)" = "x" &&
-       test "x$(svn propget svn:mime-type zot)" = "x"
+       test "x$(svn_cmd propget svn:executable exec2.sh)" = "x*" &&
+       test "x$(svn_cmd propget svn:mime-type exec2.sh)" = "x" &&
+       test "x$(svn_cmd propget svn:mime-type world.txt)" = "x" &&
+       test "x$(svn_cmd propget svn:eol-style world.txt)" = "x" &&
+       test "x$(svn_cmd propget svn:mime-type zot)" = "x"
 )
 '
 
@@ -89,12 +89,12 @@ test_expect_success 'check renamed file' '
        git svn dcommit --config-dir=user &&
        (
                cd work/svnrepo &&
-               svn up &&
+               svn_cmd up &&
                test ! -e foo &&
                test -e foo.sh &&
-               test "x$(svn propget svn:mime-type foo.sh)" = \
+               test "x$(svn_cmd propget svn:mime-type foo.sh)" = \
                     "xapplication/x-shellscript" &&
-               test "x$(svn propget svn:eol-style foo.sh)" = "xLF"
+               test "x$(svn_cmd propget svn:eol-style foo.sh)" = "xLF"
        )
 '
 
index 475c751c1cb88ce92a18beb2f9c7362a29ae4a5b..c19418614fc8946b5be5f0014559e53fcc15c060 100755 (executable)
@@ -8,11 +8,11 @@ test_expect_success 'setup svnrepo' '
        mkdir project project/trunk project/branches \
                        project/branches/v14.1 project/tags &&
        echo foo > project/trunk/foo &&
-       svn import -m "$test_description" project "$svnrepo/project" &&
+       svn_cmd import -m "$test_description" project "$svnrepo/project" &&
        rm -rf project &&
-       svn cp -m "fun" "$svnrepo/project/trunk" \
+       svn_cmd cp -m "fun" "$svnrepo/project/trunk" \
                        "$svnrepo/project/branches/v14.1/beta" &&
-       svn cp -m "more fun!" "$svnrepo/project/branches/v14.1/beta" \
+       svn_cmd cp -m "more fun!" "$svnrepo/project/branches/v14.1/beta" \
                              "$svnrepo/project/branches/v14.1/gold"
        '
 
index 87696a92dc82fad38e89a97af4a74ede84057241..4aab8ecc142d9719fd83b064445fa297cc10a520 100755 (executable)
@@ -14,21 +14,21 @@ test_expect_success 'initialize svnrepo' '
                cd trunk &&
                echo foo > foo &&
                cd .. &&
-               svn import -m "import for git-svn" . "$svnrepo" >/dev/null &&
-               svn copy "$svnrepo"/trunk "$svnrepo"/branches/a \
+               svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+               svn_cmd copy "$svnrepo"/trunk "$svnrepo"/branches/a \
                        -m "created branch a" &&
                cd .. &&
                rm -rf import &&
-               svn co "$svnrepo"/trunk trunk &&
+               svn_cmd co "$svnrepo"/trunk trunk &&
                cd trunk &&
                echo bar >> foo &&
-               svn ci -m "updated trunk" &&
+               svn_cmd ci -m "updated trunk" &&
                cd .. &&
-               svn co "$svnrepo"/branches/a a &&
+               svn_cmd co "$svnrepo"/branches/a a &&
                cd a &&
                echo baz >> a &&
-               svn add a &&
-               svn ci -m "updated a" &&
+               svn_cmd add a &&
+               svn_cmd ci -m "updated a" &&
                cd .. &&
                git svn init --stdlayout "$svnrepo"
        )
index 252daa7e1aec3c40a31db9a12a9432fbb85ef1b0..807e494a3af4eb889711cbd7bb8d20c160557301 100755 (executable)
@@ -14,13 +14,13 @@ test_expect_success 'initialize svnrepo' '
                cd trunk &&
                echo foo > foo &&
                cd .. &&
-               svn import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+               svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
                cd .. &&
                rm -rf import &&
-               svn co "$svnrepo"/trunk trunk &&
+               svn_cmd co "$svnrepo"/trunk trunk &&
                cd trunk &&
                echo bar >> foo &&
-               svn ci -m "updated trunk" &&
+               svn_cmd ci -m "updated trunk" &&
                cd .. &&
                rm -rf trunk
        )
@@ -57,14 +57,14 @@ test_expect_success 'git svn branch tests' '
 '
 
 test_expect_success 'branch uses correct svn-remote' '
-       (svn co "$svnrepo" svn &&
+       (svn_cmd co "$svnrepo" svn &&
        cd svn &&
        mkdir mirror &&
-       svn add mirror &&
-       svn copy trunk mirror/ &&
-       svn copy tags mirror/ &&
-       svn copy branches mirror/ &&
-       svn ci -m "made mirror" ) &&
+       svn_cmd add mirror &&
+       svn_cmd copy trunk mirror/ &&
+       svn_cmd copy tags mirror/ &&
+       svn_cmd copy branches mirror/ &&
+       svn_cmd ci -m "made mirror" ) &&
        rm -rf svn &&
        git svn init -s -R mirror --prefix=mirror/ "$svnrepo"/mirror &&
        git svn fetch -R mirror &&
index 3200ab38efa03076b81b94713e74205d740e38e8..b9224bdb20a397f40f1504684e1144428cf8a24e 100755 (executable)
@@ -29,16 +29,16 @@ compare_svn_head_with () {
        test_cmp current "$1"
 }
 
-for H in ISO-8859-1 EUCJP ISO-2022-JP
+for H in ISO8859-1 eucJP ISO-2022-JP
 do
        test_expect_success "$H setup" '
                mkdir $H &&
-               svn import -m "$H test" $H "$svnrepo"/$H &&
+               svn_cmd import -m "$H test" $H "$svnrepo"/$H &&
                git svn clone "$svnrepo"/$H $H
        '
 done
 
-for H in ISO-8859-1 EUCJP ISO-2022-JP
+for H in ISO8859-1 eucJP ISO-2022-JP
 do
        test_expect_success "$H commit on git side" '
        (
@@ -55,7 +55,7 @@ do
        '
 done
 
-for H in ISO-8859-1 EUCJP ISO-2022-JP
+for H in ISO8859-1 eucJP ISO-2022-JP
 do
        test_expect_success "$H dcommit to svn" '
        (
@@ -77,12 +77,12 @@ fi
 
 test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' '
        (
-               cd ISO-8859-1 &&
+               cd ISO8859-1 &&
                compare_svn_head_with "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
        )
 '
 
-for H in EUCJP ISO-2022-JP
+for H in eucJP ISO-2022-JP
 do
        test_expect_success UTF8 "$H should match UTF-8 in svn" '
                (
index b8fb277562e7782cf538ba7011214b4cf9136484..f5abdb3c7fd24033f1538db51c957345ab86e341 100755 (executable)
@@ -15,7 +15,7 @@ EOF
 test_expect_success 'setup svnrepo' '
        for i in aa bb cc dd
        do
-               svn mkdir -m $i --username $i "$svnrepo"/$i
+               svn_cmd mkdir -m $i --username $i "$svnrepo"/$i
        done
        '
 
@@ -52,13 +52,13 @@ test_expect_success 'continues to import once authors have been added' '
        '
 
 test_expect_success 'authors-file against globs' '
-       svn mkdir -m globs --username aa \
+       svn_cmd mkdir -m globs --username aa \
          "$svnrepo"/aa/trunk "$svnrepo"/aa/branches "$svnrepo"/aa/tags &&
        git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work &&
        for i in bb ee cc
        do
                branch="aa/branches/$i"
-               svn mkdir -m "$branch" --username $i "$svnrepo/$branch"
+               svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch"
        done
        '
 
index 893f57ef7370a5078cce39ef7f4136342451f655..f3c30e63b7f584cddb91793b0170f004d866530d 100755 (executable)
@@ -7,19 +7,19 @@ test_description='git svn property tests'
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repo with a git repo inside it' '
-       svn co "$svnrepo" s &&
+       svn_cmd co "$svnrepo" s &&
        (
                cd s &&
                git init &&
                test -f .git/HEAD &&
                > .git/a &&
                echo a > a &&
-               svn add .git a &&
-               svn commit -m "create a nested git repo" &&
-               svn up &&
+               svn_cmd add .git a &&
+               svn_cmd commit -m "create a nested git repo" &&
+               svn_cmd up &&
                echo hi >> .git/a &&
-               svn commit -m "modify .git/a" &&
-               svn up
+               svn_cmd commit -m "modify .git/a" &&
+               svn_cmd up
        )
 '
 
@@ -33,9 +33,9 @@ test_expect_success 'SVN-side change outside of .git' '
        (
                cd s &&
                echo b >> a &&
-               svn commit -m "SVN-side change outside of .git" &&
-               svn up &&
-               svn log -v | fgrep "SVN-side change outside of .git"
+               svn_cmd commit -m "SVN-side change outside of .git" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change outside of .git"
        )
 '
 
@@ -56,10 +56,10 @@ test_expect_success 'SVN-side change inside of .git' '
                git add a &&
                git commit -m "add a inside an SVN repo" &&
                git log &&
-               svn add --force .git &&
-               svn commit -m "SVN-side change inside of .git" &&
-               svn up &&
-               svn log -v | fgrep "SVN-side change inside of .git"
+               svn_cmd add --force .git &&
+               svn_cmd commit -m "SVN-side change inside of .git" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change inside of .git"
        )
 '
 
@@ -80,9 +80,9 @@ test_expect_success 'SVN-side change in and out of .git' '
                echo c >> a &&
                git add a &&
                git commit -m "add a inside an SVN repo" &&
-               svn commit -m "SVN-side change in and out of .git" &&
-               svn up &&
-               svn log -v | fgrep "SVN-side change in and out of .git"
+               svn_cmd commit -m "SVN-side change in and out of .git" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change in and out of .git"
        )
 '
 
index 71fdc4a69dde2ff0bfdb092d8e28aeba495a1dc0..09ff10cd9b0688522671cac9ee2c8886a8587947 100755 (executable)
@@ -8,19 +8,19 @@ test_description='git svn property tests'
 . ./lib-git-svn.sh
 
 test_expect_success 'setup test repository' '
-       svn co "$svnrepo" s &&
+       svn_cmd co "$svnrepo" s &&
        (
                cd s &&
                mkdir qqq www &&
                echo test_qqq > qqq/test_qqq.txt &&
                echo test_www > www/test_www.txt &&
-               svn add qqq &&
-               svn add www &&
-               svn commit -m "create some files" &&
-               svn up &&
+               svn_cmd add qqq &&
+               svn_cmd add www &&
+               svn_cmd commit -m "create some files" &&
+               svn_cmd up &&
                echo hi >> www/test_www.txt &&
-               svn commit -m "modify www/test_www.txt" &&
-               svn up
+               svn_cmd commit -m "modify www/test_www.txt" &&
+               svn_cmd up
        )
 '
 
@@ -51,9 +51,9 @@ test_expect_success 'SVN-side change outside of www' '
        (
                cd s &&
                echo b >> qqq/test_qqq.txt &&
-               svn commit -m "SVN-side change outside of www" &&
-               svn up &&
-               svn log -v | fgrep "SVN-side change outside of www"
+               svn_cmd commit -m "SVN-side change outside of www" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change outside of www"
        )
 '
 
@@ -83,9 +83,9 @@ test_expect_success 'SVN-side change inside of ignored www' '
        (
                cd s &&
                echo zaq >> www/test_www.txt
-               svn commit -m "SVN-side change inside of www/test_www.txt" &&
-               svn up &&
-               svn log -v | fgrep "SVN-side change inside of www/test_www.txt"
+               svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
        )
 '
 
@@ -116,9 +116,9 @@ test_expect_success 'SVN-side change in and out of ignored www' '
                cd s &&
                echo cvf >> www/test_www.txt
                echo ygg >> qqq/test_qqq.txt
-               svn commit -m "SVN-side change in and out of ignored www" &&
-               svn up &&
-               svn log -v | fgrep "SVN-side change in and out of ignored www"
+               svn_cmd commit -m "SVN-side change in and out of ignored www" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
        )
 '
 
index fd185011b73a8e45b7863bc336f884dee8d4f980..636ca0abb965ad2462a2e1e1ebea2a4183aa19e6 100755 (executable)
@@ -8,7 +8,7 @@ test_expect_success 'initialize repo' '
        mkdir import &&
        cd import &&
        awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
-       svn import -m "initial" . "$svnrepo" &&
+       svn_cmd import -m "initial" . "$svnrepo" &&
        cd .. &&
        git svn init "$svnrepo" &&
        git svn fetch &&
@@ -18,14 +18,14 @@ test_expect_success 'initialize repo' '
 test_expect_success '(supposedly) non-conflicting change from SVN' '
        test x"`sed -n -e 58p < file`" = x58 &&
        test x"`sed -n -e 61p < file`" = x61 &&
-       svn co "$svnrepo" tmp &&
+       svn_cmd co "$svnrepo" tmp &&
        cd tmp &&
                perl -i.bak -p -e "s/^58$/5588/" file &&
                perl -i.bak -p -e "s/^61$/6611/" file &&
                poke file &&
                test x"`sed -n -e 58p < file`" = x5588 &&
                test x"`sed -n -e 61p < file`" = x6611 &&
-               svn commit -m "58 => 5588, 61 => 6611" &&
+               svn_cmd commit -m "58 => 5588, 61 => 6611" &&
                cd ..
        '
 
@@ -46,7 +46,7 @@ test_expect_success 'change file but in unrelated area' "
        test x\"\`sed -n -e 7p < file\`\" = x7777 &&
        git commit -m '4 => 4444, 7 => 7777' file &&
        git svn dcommit &&
-       svn up tmp &&
+       svn_cmd up tmp &&
        cd tmp &&
                test x\"\`sed -n -e 4p < file\`\" = x4444 &&
                test x\"\`sed -n -e 7p < file\`\" = x7777 &&
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
new file mode 100755 (executable)
index 0000000..a4b00f2
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong, Mark Lodato
+#
+
+test_description='git svn authors prog tests'
+
+. ./lib-git-svn.sh
+
+cat > svn-authors-prog <<'EOF'
+#!/usr/bin/perl
+$_ = shift;
+if (s/-sub$//)  {
+       print "$_ <$_\@sub.example.com>\n";
+}
+else {
+       print "$_ <$_\@example.com>\n";
+}
+EOF
+chmod +x svn-authors-prog
+
+cat > svn-authors <<'EOF'
+ff = FFFFFFF FFFFFFF <fFf@other.example.com>
+EOF
+
+test_expect_success 'setup svnrepo' '
+       for i in aa bb cc-sub dd-sub ee-foo ff
+       do
+               svn mkdir -m $i --username $i "$svnrepo"/$i
+       done
+       '
+
+test_expect_success 'import authors with prog and file' '
+       git svn clone --authors-prog=./svn-authors-prog \
+           --authors-file=svn-authors "$svnrepo" x
+       '
+
+test_expect_success 'imported 6 revisions successfully' '
+       (
+               cd x
+               test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 6
+       )
+       '
+
+test_expect_success 'authors-prog ran correctly' '
+       (
+               cd x
+               git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+                 grep "^author ee-foo <ee-foo@example\.com> " &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn~2 | \
+                 grep "^author dd <dd@sub\.example\.com> " &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn~3 | \
+                 grep "^author cc <cc@sub\.example\.com> " &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn~4 | \
+                 grep "^author bb <bb@example\.com> " &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn~5 | \
+                 grep "^author aa <aa@example\.com> "
+       )
+       '
+
+test_expect_success 'authors-file overrode authors-prog' '
+       (
+               cd x
+               git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+                 grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> "
+       )
+       '
+
+test_done
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
new file mode 100755 (executable)
index 0000000..f337959
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn refuses to dcommit non-UTF8 messages'
+
+. ./lib-git-svn.sh
+
+# ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
+
+for H in ISO8859-1 eucJP
+do
+       test_expect_success "$H setup" '
+               mkdir $H &&
+               svn_cmd import -m "$H test" $H "$svnrepo"/$H &&
+               git svn clone "$svnrepo"/$H $H
+       '
+done
+
+for H in ISO8859-1 eucJP
+do
+       test_expect_success "$H commit on git side" '
+       (
+               cd $H &&
+               git config i18n.commitencoding $H &&
+               git checkout -b t refs/remotes/git-svn &&
+               echo $H >F &&
+               git add F &&
+               git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
+               E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
+               test "z$E" = "z$H"
+       )
+       '
+done
+
+for H in ISO8859-1 eucJP
+do
+       test_expect_success "$H dcommit to svn" '
+       (
+               cd $H &&
+               git config --unset i18n.commitencoding &&
+               ! git svn dcommit
+       )
+       '
+done
+
+test_done
index 56b7c06921d9ef3b72ff3ee6f62f7a1c426b3028..ef1f8d22f67187089ff88811457d8e26cf354047 100755 (executable)
@@ -317,4 +317,22 @@ test_expect_success 'use the same checkout for Git and CVS' '
 
 '
 
+test_expect_success 're-commit a removed filename which remains in CVS attic' '
+
+    (cd "$CVSWORK" &&
+     echo >attic_gremlin &&
+     cvs -Q add attic_gremlin &&
+     cvs -Q ci -m "added attic_gremlin" &&
+     rm attic_gremlin &&
+     cvs -Q rm attic_gremlin &&
+     cvs -Q ci -m "removed attic_gremlin") &&
+
+    echo > attic_gremlin &&
+    git add attic_gremlin &&
+    git commit -m "Added attic_gremlin" &&
+       git cvsexportcommit -w "$CVSWORK" -c HEAD &&
+    (cd "$CVSWORK"; cvs -Q update -d) &&
+    test -f "$CVSWORK/attic_gremlin"
+'
+
 test_done
index 8da9ce54596416af6fc9779a5ac3f7131958aec8..8c8a9e63c26c594a837d266bff56f5f85cb376c8 100755 (executable)
@@ -68,7 +68,7 @@ test_expect_success 'fast-export master~2..master' '
 
 test_expect_success 'iso-8859-1' '
 
-       git config i18n.commitencoding ISO-8859-1 &&
+       git config i18n.commitencoding ISO8859-1 &&
        # use author and committer name in ISO-8859-1 to match it.
        . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
        test_tick &&
index f4210fbb04065cfe32f26053eb5f5f054d52e3cf..d539619e89c5263e3b8339c408f1c0dcd867457b 100755 (executable)
@@ -590,7 +590,7 @@ test_expect_success \
         echo "ISO-8859-1" >> file &&
         git add file &&
         git config i18n.commitencoding ISO-8859-1 &&
-        git commit -F "$TEST_DIRECTORY"/t3900/ISO-8859-1.txt &&
+        git commit -F "$TEST_DIRECTORY"/t3900/ISO8859-1.txt &&
         git config --unset i18n.commitencoding &&
         gitweb_run "p=.git;a=commit"'
 test_debug 'cat gitweb.log'
index b4ca244626a6635faa2587722c26f4c17692af65..4eb7d3f7f042f896106f208a7f8bb91925352b2a 100755 (executable)
@@ -29,6 +29,10 @@ test_expect_success \
      git add . &&
      git commit -m "first commit" &&
 
+     echo "new file in subdir 2" > directory2/file2 &&
+     git add . &&
+     git commit -m "commit in directory2" &&
+
      echo "changed file 1" > file1 &&
      git commit -a -m "second commit" &&
 
index 697daf3ffd33c27654ce00f780acc2c6db5f9985..6c70aec020c9c6486ae984dc264edb1dd8b270cf 100755 (executable)
@@ -86,15 +86,22 @@ close TEMPFILE;
 unlink $tmpfile;
 
 # paths
-is($r->repo_path, "./.git", "repo_path");
+is($r->repo_path, $abs_repo_dir . "/.git", "repo_path");
 is($r->wc_path, $abs_repo_dir . "/", "wc_path");
 is($r->wc_subdir, "", "wc_subdir initial");
 $r->wc_chdir("directory1");
 is($r->wc_subdir, "directory1", "wc_subdir after wc_chdir");
-TODO: {
-       local $TODO = "commands do not work after wc_chdir";
-       # Failure output is active even in non-verbose mode and thus
-       # annoying.  Hence we skip these tests as long as they fail.
-       todo_skip 'config after wc_chdir', 1;
-       is($r->config("color.string"), "value", "config after wc_chdir");
-}
+is($r->config("test.string"), "value", "config after wc_chdir");
+
+# Object generation in sub directory
+chdir("directory2");
+my $r2 = Git->repository();
+is($r2->repo_path, $abs_repo_dir . "/.git", "repo_path (2)");
+is($r2->wc_path, $abs_repo_dir . "/", "wc_path (2)");
+is($r2->wc_subdir, "directory2/", "wc_subdir initial (2)");
+
+# commands in sub directory
+my $last_commit = $r2->command_oneline(qw(rev-parse --verify HEAD));
+like($last_commit, qr/^[0-9a-fA-F]{40}$/, 'rev-parse returned hash');
+my $dir_commit = $r2->command_oneline('log', '-n1', '--pretty=format:%H', '.');
+isnt($last_commit, $dir_commit, 'log . does not show last commit');
index dad1437fa49596cf6f36b40b1ab18b008620a246..5fdc5d94a20cfcf231bf23590784c5146a3e44bc 100644 (file)
@@ -115,7 +115,7 @@ do
        --tee)
                shift ;; # was handled already
        *)
-               break ;;
+               echo "error: unknown test option '$1'" >&2; exit 1 ;;
        esac
 done
 
@@ -147,7 +147,7 @@ fi
 
 error () {
        say_color error "error: $*"
-       trap - EXIT
+       GIT_EXIT_OK=t
        exit 1
 }
 
@@ -179,10 +179,17 @@ test_broken=0
 test_success=0
 
 die () {
-       echo >&5 "FATAL: Unexpected exit with code $?"
-       exit 1
+       code=$?
+       if test -n "$GIT_EXIT_OK"
+       then
+               exit $code
+       else
+               echo >&5 "FATAL: Unexpected exit with code $code"
+               exit 1
+       fi
 }
 
+GIT_EXIT_OK=
 trap 'die' EXIT
 
 # The semantics of the editor variables are that of invoking
@@ -285,7 +292,7 @@ test_failure_ () {
        say_color error "FAIL $test_count: $1"
        shift
        echo "$@" | sed -e 's/^/        /'
-       test "$immediate" = "" || { trap - EXIT; exit 1; }
+       test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
 }
 
 test_known_broken_ok_ () {
@@ -347,7 +354,7 @@ test_expect_failure () {
                then
                        test_known_broken_ok_ "$1"
                else
-                   test_known_broken_failure_ "$1"
+                       test_known_broken_failure_ "$1"
                fi
        fi
        echo >&3 ""
@@ -498,7 +505,7 @@ test_create_repo () {
 }
 
 test_done () {
-       trap - EXIT
+       GIT_EXIT_OK=t
        test_results_dir="$TEST_DIRECTORY/test-results"
        mkdir -p "$test_results_dir"
        test_results_path="$test_results_dir/${0%.sh}-$$"
@@ -640,7 +647,7 @@ fi
 test="trash directory.$(basename "$0" .sh)"
 test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
 rm -fr "$test" || {
-       trap - EXIT
+       GIT_EXIT_OK=t
        echo >&5 "FATAL: Cannot prepare test area"
        exit 1
 }
index 0ba62076fb4a381e588eba90ed639dd560860fd3..b11ad6a6fbab9242a2dac157ee1a55875954b95a 100755 (executable)
@@ -7,6 +7,31 @@
 #
 # To enable this hook, rename this file to "pre-commit".
 
+# If you want to allow non-ascii filenames set this variable to true.
+allownonascii=$(git config hooks.allownonascii)
+
+# Cross platform projects tend to avoid non-ascii filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+       test "$(git diff --cached --name-only --diff-filter=A -z |
+         LC_ALL=C tr -d '[ -~]\0')"
+then
+       echo "Error: Attempt to add a non-ascii filename."
+       echo
+       echo "This can cause problems if you want to work together"
+       echo "with people on other platforms than you."
+       echo
+       echo "To be portable it is adviseable to rename the file ..."
+       echo
+       echo "If you know what you are doing you can disable this"
+       echo "check using:"
+       echo
+       echo "  git config hooks.allownonascii true"
+       echo
+       exit 1
+fi
+
 if git-rev-parse --verify HEAD >/dev/null 2>&1
 then
        against=HEAD
index f8bf490cffa2d41d19ffc9a31839ca3c2686df16..fd63b2d662dbcf98ec622a1ab754d041a559e3be 100755 (executable)
@@ -13,6 +13,9 @@
 # hooks.allowdeletetag
 #   This boolean sets whether deleting tags will be allowed in the
 #   repository.  By default they won't be.
+# hooks.allowmodifytag
+#   This boolean sets whether a tag may be modified after creation. By default
+#   it won't be.
 # hooks.allowdeletebranch
 #   This boolean sets whether deleting branches will be allowed in the
 #   repository.  By default they won't be.
@@ -44,6 +47,7 @@ allowunannotated=$(git config --bool hooks.allowunannotated)
 allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
 denycreatebranch=$(git config --bool hooks.denycreatebranch)
 allowdeletetag=$(git config --bool hooks.allowdeletetag)
+allowmodifytag=$(git config --bool hooks.allowmodifytag)
 
 # check for no description
 projectdesc=$(sed -e '1q' "$GIT_DIR/description")
@@ -82,6 +86,12 @@ case "$refname","$newrev_type" in
                ;;
        refs/tags/*,tag)
                # annotated tag
+               if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+               then
+                       echo "*** Tag '$refname' already exists." >&2
+                       echo "*** Modifying a tag is not allowed in this repository." >&2
+                       exit 1
+               fi
                ;;
        refs/heads/*,commit)
                # branch
index d5358cbaac2022483b74366555fc9707a7d8ad97..fe476cb6185a389671c014b4ea067184f51465d0 100644 (file)
@@ -87,6 +87,15 @@ int main(int argc, const char *argv[])
                        return -1;
                }
 
+#ifdef WIN32
+               if (!(sb.st_mode & S_IWUSR) &&
+                               chmod(argv[i], sb.st_mode | S_IWUSR)) {
+                       fprintf(stderr, "Could not make user-writable %s: %s",
+                               argv[i], strerror(errno));
+                       return -1;
+               }
+#endif
+
                utb.actime = sb.st_atime;
                utb.modtime = set_eq ? set_time : sb.st_mtime + set_time;
 
index 61d2c39814529bd0264e4c9e40241131d51d819c..a90bc3003d97f86fcc2210e340666106647eb74c 100644 (file)
@@ -7,6 +7,7 @@ static unsigned long timestamp;
 static int abbrev = 7;
 static int verbose = 0, dry_run = 0, quiet = 0;
 static char *string = NULL;
+static char *file = NULL;
 
 int length_callback(const struct option *opt, const char *arg, int unset)
 {
@@ -19,8 +20,15 @@ int length_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+int number_callback(const struct option *opt, const char *arg, int unset)
+{
+       *(int *)opt->value = strtol(arg, NULL, 10);
+       return 0;
+}
+
 int main(int argc, const char **argv)
 {
+       const char *prefix = "prefix/";
        const char *usage[] = {
                "test-parse-options <options>",
                NULL
@@ -29,6 +37,7 @@ int main(int argc, const char **argv)
                OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"),
                OPT_BIT('4', "or4", &boolean,
                        "bitwise-or boolean with ...0100", 4),
+               OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4),
                OPT_GROUP(""),
                OPT_INTEGER('i', "integer", &integer, "get a integer"),
                OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
@@ -36,6 +45,7 @@ int main(int argc, const char **argv)
                OPT_DATE('t', NULL, &timestamp, "get timestamp of <time>"),
                OPT_CALLBACK('L', "length", &integer, "str",
                        "get length of <str>", length_callback),
+               OPT_FILENAME('F', "file", &file, "set file to <FILE>"),
                OPT_GROUP("String options"),
                OPT_STRING('s', "string", &string, "string", "get a string"),
                OPT_STRING(0, "string2", &string, "str", "get another string"),
@@ -45,6 +55,10 @@ int main(int argc, const char **argv)
                        "set string to default", (unsigned long)"default"),
                OPT_GROUP("Magic arguments"),
                OPT_ARGUMENT("quux", "means --quux"),
+               OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
+                       number_callback),
+               { OPTION_BOOLEAN, '+', NULL, &boolean, NULL, "same as -b",
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH },
                OPT_GROUP("Standard options"),
                OPT__ABBREV(&abbrev),
                OPT__VERBOSE(&verbose),
@@ -54,7 +68,7 @@ int main(int argc, const char **argv)
        };
        int i;
 
-       argc = parse_options(argc, argv, options, usage, 0);
+       argc = parse_options(argc, argv, prefix, options, usage, 0);
 
        printf("boolean: %d\n", boolean);
        printf("integer: %u\n", integer);
@@ -64,6 +78,7 @@ int main(int argc, const char **argv)
        printf("verbose: %d\n", verbose);
        printf("quiet: %s\n", quiet ? "yes" : "no");
        printf("dry run: %s\n", dry_run ? "yes" : "no");
+       printf("file: %s\n", file ? file : "(not set)");
 
        for (i = 0; i < argc; i++)
                printf("arg %02d: %s\n", i, argv[i]);
index efecb65258ba95ee743f0e3ca178e74e2756e7d1..501a77b2418c4c5f5ca49a9fdecba3165129c1b2 100644 (file)
@@ -439,9 +439,7 @@ static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
        char *ref_name;
        char *refs_url;
        int i = 0;
-
-       struct active_request_slot *slot;
-       struct slot_results results;
+       int http_ret;
 
        struct ref *refs = NULL;
        struct ref *ref = NULL;
@@ -461,25 +459,16 @@ static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
        refs_url = xmalloc(strlen(transport->url) + 11);
        sprintf(refs_url, "%s/info/refs", transport->url);
 
-       slot = get_active_slot();
-       slot->results = &results;
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
-
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (results.curl_result != CURLE_OK) {
-                       strbuf_release(&buffer);
-                       if (missing_target(&results))
-                               die("%s not found: did you run git update-server-info on the server?", refs_url);
-                       else
-                               die("%s download error - %s", refs_url, curl_errorstr);
-               }
-       } else {
-               strbuf_release(&buffer);
-               die("Unable to start HTTP request");
+       http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
+       switch (http_ret) {
+       case HTTP_OK:
+               break;
+       case HTTP_MISSING_TARGET:
+               die("%s not found: did you run git update-server-info on the"
+                   " server?", refs_url);
+       default:
+               http_error(refs_url, http_ret);
+               die("HTTP request failed");
        }
 
        data = buffer.buf;
@@ -519,6 +508,8 @@ static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
                free(ref);
        }
 
+       strbuf_release(&buffer);
+       free(refs_url);
        return refs;
 }
 
@@ -732,9 +723,9 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
 {
        fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
        if (from)
-               fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+               fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
        else
-               fputs(prettify_ref(to), stderr);
+               fputs(prettify_refname(to->name), stderr);
        if (msg) {
                fputs(" (", stderr);
                fputs(msg, stderr);
@@ -1003,7 +994,6 @@ int transport_push(struct transport *transport,
        if (transport->push_refs) {
                struct ref *remote_refs =
                        transport->get_refs_list(transport, 1);
-               struct ref **remote_tail;
                struct ref *local_refs = get_local_heads();
                int match_flags = MATCH_REFS_NONE;
                int verbose = flags & TRANSPORT_PUSH_VERBOSE;
@@ -1014,10 +1004,7 @@ int transport_push(struct transport *transport,
                if (flags & TRANSPORT_PUSH_MIRROR)
                        match_flags |= MATCH_REFS_MIRROR;
 
-               remote_tail = &remote_refs;
-               while (*remote_tail)
-                       remote_tail = &((*remote_tail)->next);
-               if (match_refs(local_refs, remote_refs, &remote_tail,
+               if (match_refs(local_refs, &remote_refs,
                               refspec_nr, refspec, match_flags)) {
                        return -1;
                }
@@ -1083,3 +1070,51 @@ int transport_disconnect(struct transport *transport)
        free(transport);
        return ret;
 }
+
+/*
+ * Strip username (and password) from an url and return
+ * it in a newly allocated string.
+ */
+char *transport_anonymize_url(const char *url)
+{
+       char *anon_url, *scheme_prefix, *anon_part;
+       size_t anon_len, prefix_len = 0;
+
+       anon_part = strchr(url, '@');
+       if (is_local(url) || !anon_part)
+               goto literal_copy;
+
+       anon_len = strlen(++anon_part);
+       scheme_prefix = strstr(url, "://");
+       if (!scheme_prefix) {
+               if (!strchr(anon_part, ':'))
+                       /* cannot be "me@there:/path/name" */
+                       goto literal_copy;
+       } else {
+               const char *cp;
+               /* make sure scheme is reasonable */
+               for (cp = url; cp < scheme_prefix; cp++) {
+                       switch (*cp) {
+                               /* RFC 1738 2.1 */
+                       case '+': case '.': case '-':
+                               break; /* ok */
+                       default:
+                               if (isalnum(*cp))
+                                       break;
+                               /* it isn't */
+                               goto literal_copy;
+                       }
+               }
+               /* @ past the first slash does not count */
+               cp = strchr(scheme_prefix + 3, '/');
+               if (cp && cp < anon_part)
+                       goto literal_copy;
+               prefix_len = scheme_prefix - url + 3;
+       }
+       anon_url = xcalloc(1, 1 + prefix_len + anon_len);
+       memcpy(anon_url, url, prefix_len);
+       memcpy(anon_url + prefix_len, anon_part, anon_len);
+       return anon_url;
+literal_copy:
+       return xstrdup(url);
+}
index b1c225276619c3bc4dda5dcd0469e242ab421ea0..27bfc528acb5b51006c7d61db0d1d7910adaf31c 100644 (file)
@@ -74,5 +74,6 @@ const struct ref *transport_get_remote_refs(struct transport *transport);
 int transport_fetch_refs(struct transport *transport, const struct ref *refs);
 void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
+char *transport_anonymize_url(const char *url);
 
 #endif
index d556da975197a718979575864e37c2b9b9f3327a..57529ae63d49826952b29860b3d4106b60250c7b 100644 (file)
@@ -13,7 +13,8 @@ PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
         "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
 PATTERNS("java",
         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
-        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
+        "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
+        /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]="
@@ -25,7 +26,7 @@ PATTERNS("objc",
         /* Objective-C methods */
         "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
         /* C functions */
-        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
+        "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$\n"
         /* Objective-C class/protocol definitions */
         "^(@(implementation|interface|protocol)[ \t].*)$",
         /* -- */
diff --git a/utf8.c b/utf8.c
index ddfdc5e2b88d346886f64e39a93ced85cc10a78e..db706ac4ed6f033091cfaeb7b23c07ca7fd71675 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -354,7 +354,7 @@ int is_encoding_utf8(const char *name)
  * with iconv.  If the conversion fails, returns NULL.
  */
 #ifndef NO_ICONV
-#ifdef OLD_ICONV
+#if defined(OLD_ICONV) || (defined(__sun__) && !defined(_XPG6))
        typedef const char * iconv_ibp;
 #else
        typedef char * iconv_ibp;