]> asedeno.scripts.mit.edu Git - git.git/commitdiff
Merge branch 'jn/maint-1.6.3-check-ref-format-doc'
authorJunio C Hamano <gitster@pobox.com>
Mon, 19 Oct 2009 06:01:26 +0000 (23:01 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 19 Oct 2009 06:01:26 +0000 (23:01 -0700)
* jn/maint-1.6.3-check-ref-format-doc:
  Documentation: describe check-ref-format --branch

590 files changed:
.gitattributes
.gitignore
.mailmap
Documentation/Makefile
Documentation/RelNotes-1.6.3.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.3.2.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.3.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.3.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.4.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.4.2.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.4.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.4.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.5.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.5.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.6.txt [new file with mode: 0644]
Documentation/asciidoc.conf
Documentation/config.txt
Documentation/diff-format.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-apply.txt
Documentation/git-archive.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-cat-file.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout.txt
Documentation/git-clean.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-config.txt
Documentation/git-cvsexportcommit.txt
Documentation/git-diff-files.txt
Documentation/git-diff-index.txt
Documentation/git-diff-tree.txt
Documentation/git-diff.txt
Documentation/git-difftool.txt
Documentation/git-fast-export.txt
Documentation/git-filter-branch.txt
Documentation/git-fmt-merge-msg.txt
Documentation/git-format-patch.txt
Documentation/git-gc.txt
Documentation/git-grep.txt
Documentation/git-init-db.txt
Documentation/git-init.txt
Documentation/git-instaweb.txt
Documentation/git-log.txt
Documentation/git-ls-files.txt
Documentation/git-ls-tree.txt
Documentation/git-mailinfo.txt
Documentation/git-merge-base.txt
Documentation/git-merge.txt
Documentation/git-mergetool.txt
Documentation/git-mktree.txt
Documentation/git-mv.txt
Documentation/git-pack-objects.txt
Documentation/git-parse-remote.txt
Documentation/git-prune-packed.txt
Documentation/git-push.txt
Documentation/git-quiltimport.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-remote-helpers.txt [new file with mode: 0644]
Documentation/git-remote.txt
Documentation/git-repack.txt
Documentation/git-replace.txt [new file with mode: 0644]
Documentation/git-rerere.txt
Documentation/git-reset.txt
Documentation/git-rev-list.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-show-branch.txt
Documentation/git-show-ref.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-symbolic-ref.txt
Documentation/git-tag.txt
Documentation/git-upload-pack.txt
Documentation/git-verify-pack.txt
Documentation/git-write-tree.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcli.txt
Documentation/gitcore-tutorial.txt
Documentation/githooks.txt
Documentation/gitmodules.txt
Documentation/gittutorial.txt
Documentation/glossary-content.txt
Documentation/merge-config.txt
Documentation/merge-options.txt
Documentation/pt_BR/gittutorial.txt [new file with mode: 0644]
Documentation/rev-list-options.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/api-remote.txt
Documentation/technical/api-run-command.txt
Documentation/technical/api-tree-walking.txt
Documentation/technical/racy-git.txt
Documentation/urls-remotes.txt
Documentation/urls.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
INSTALL
Makefile
README
RelNotes
abspath.c
advice.c [new file with mode: 0644]
advice.h [new file with mode: 0644]
alias.c
archive-tar.c
archive.c
arm/sha1.c [deleted file]
arm/sha1.h [deleted file]
arm/sha1_arm.S [deleted file]
attr.c
base85.c
bisect.c
bisect.h
block-sha1/sha1.c [new file with mode: 0644]
block-sha1/sha1.h [new file with mode: 0644]
branch.c
builtin-add.c
builtin-apply.c
builtin-archive.c
builtin-bisect--helper.c
builtin-blame.c
builtin-branch.c
builtin-bundle.c
builtin-cat-file.c
builtin-check-attr.c
builtin-checkout-index.c
builtin-checkout.c
builtin-clean.c
builtin-clone.c
builtin-commit-tree.c
builtin-commit.c
builtin-config.c
builtin-count-objects.c
builtin-describe.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch--tool.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-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-mailinfo.c
builtin-mailsplit.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-packed.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-receive-pack.c
builtin-reflog.c
builtin-remote.c
builtin-replace.c [new file with mode: 0644]
builtin-rerere.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-show-ref.c
builtin-stripspace.c
builtin-symbolic-ref.c
builtin-tag.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-update-ref.c
builtin-update-server-info.c [new file with mode: 0644]
builtin-upload-archive.c
builtin-verify-pack.c
builtin-verify-tag.c
builtin-write-tree.c
builtin.h
bundle.c
cache-tree.c
cache-tree.h
cache.h
combine-diff.c
command-list.txt
commit.c
commit.h
compat/basename.c [new file with mode: 0644]
compat/bswap.h [new file with mode: 0644]
compat/mingw.c
compat/mingw.h
compat/mkstemps.c [new file with mode: 0644]
compat/msvc.c [new file with mode: 0644]
compat/msvc.h [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/regex/regex.c
compat/snprintf.c
compat/vcbuild/README [new file with mode: 0644]
compat/vcbuild/include/alloca.h [new file with mode: 0644]
compat/vcbuild/include/arpa/inet.h [new file with mode: 0644]
compat/vcbuild/include/dirent.h [new file with mode: 0644]
compat/vcbuild/include/grp.h [new file with mode: 0644]
compat/vcbuild/include/inttypes.h [new file with mode: 0644]
compat/vcbuild/include/netdb.h [new file with mode: 0644]
compat/vcbuild/include/netinet/in.h [new file with mode: 0644]
compat/vcbuild/include/netinet/tcp.h [new file with mode: 0644]
compat/vcbuild/include/pwd.h [new file with mode: 0644]
compat/vcbuild/include/sys/ioctl.h [new file with mode: 0644]
compat/vcbuild/include/sys/param.h [new file with mode: 0644]
compat/vcbuild/include/sys/poll.h [new file with mode: 0644]
compat/vcbuild/include/sys/select.h [new file with mode: 0644]
compat/vcbuild/include/sys/socket.h [new file with mode: 0644]
compat/vcbuild/include/sys/time.h [new file with mode: 0644]
compat/vcbuild/include/sys/utime.h [new file with mode: 0644]
compat/vcbuild/include/sys/wait.h [new file with mode: 0644]
compat/vcbuild/include/unistd.h [new file with mode: 0644]
compat/vcbuild/include/utime.h [new file with mode: 0644]
compat/vcbuild/scripts/clink.pl [new file with mode: 0644]
compat/vcbuild/scripts/lib.pl [new file with mode: 0644]
compat/win32.h
compat/winansi.c
config.c
config.mak.in
configure.ac
connect.c
contrib/buildsystems/Generators.pm [new file with mode: 0644]
contrib/buildsystems/Generators/QMake.pm [new file with mode: 0644]
contrib/buildsystems/Generators/Vcproj.pm [new file with mode: 0644]
contrib/buildsystems/engine.pl [new file with mode: 0644]
contrib/buildsystems/generate [new file with mode: 0644]
contrib/buildsystems/parse.pl [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/emacs/git-blame.el
contrib/emacs/git.el
contrib/fast-import/git-p4
contrib/fast-import/import-directories.perl [new file with mode: 0755]
contrib/fast-import/import-tars.perl
contrib/hg-to-git/hg-to-git.py
contrib/hooks/post-receive-email [changed mode: 0644->0755]
convert.c
copy.c
csum-file.c
daemon.c
date.c
decorate.c
delta.h
diff-delta.c
diff-lib.c
diff.c
dir.c
dir.h
entry.c
environment.c
fast-import.c
fsck.c
git-add--interactive.perl
git-am.sh
git-bisect.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-instaweb.sh
git-merge-one-file.sh
git-mergetool--lib.sh
git-parse-remote.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-repack.sh
git-request-pull.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
git.spec.in
gitk-git/gitk
gitk-git/po/de.po
gitk-git/po/sv.po
gitweb/INSTALL
gitweb/README
gitweb/git-favicon.png
gitweb/git-logo.png
gitweb/gitweb.css
gitweb/gitweb.perl
graph.c
grep.c
grep.h
hash-object.c
help.c
http-fetch.c [moved from builtin-http-fetch.c with 95% similarity]
http-push.c
http-walker.c
http.c
http.h
imap-send.c
index-pack.c
ll-merge.c
lockfile.c
log-tree.c
log-tree.h
mailmap.c
merge-index.c
merge-recursive.c
mktag.c
mktree.c [deleted file]
mozilla-sha1/sha1.c [deleted file]
mozilla-sha1/sha1.h [deleted file]
object.c
pack-check.c
pack-redundant.c
pack-refs.c
pack-revindex.c
pack-write.c
pager.c
parse-options.c
parse-options.h
patch-delta.c
path.c
perl/Git.pm
perl/Makefile
perl/Makefile.PL
pkt-line.c
preload-index.c
pretty.c
progress.c
quote.c
read-cache.c
refs.c
refs.h
remote-curl.c [new file with mode: 0644]
remote.c
remote.h
replace_object.c [new file with mode: 0644]
rerere.c
revision.c
revision.h
run-command.c
run-command.h
send-pack.h
server-info.c
setup.c
sha1_file.c
sha1_name.c
shell.c
strbuf.c
strbuf.h
symlinks.c
t/Makefile
t/gitweb-lib.sh [new file with mode: 0644]
t/lib-cvs.sh [new file with mode: 0644]
t/lib-git-svn.sh
t/lib-httpd.sh
t/lib-patch-mode.sh [new file with mode: 0755]
t/t0001-init.sh
t/t0006-date.sh [new file with mode: 0755]
t/t0040-parse-options.sh
t/t1009-read-tree-new-index.sh [new file with mode: 0755]
t/t1010-mktree.sh [new file with mode: 0755]
t/t1300-repo-config.sh
t/t1502-rev-parse-parseopt.sh
t/t2000-checkout-cache-clash.sh
t/t2014-switch.sh [new file with mode: 0755]
t/t2015-checkout-unborn.sh [new file with mode: 0755]
t/t2016-checkout-patch.sh [new file with mode: 0755]
t/t3003-ls-files-exclude.sh [new file with mode: 0755]
t/t3030-merge-recursive.sh
t/t3031-merge-criscross.sh [new file with mode: 0755]
t/t3202-show-branch-octopus.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3409-rebase-preserve-merges.sh
t/t3411-rebase-preserve-around-merges.sh
t/t3414-rebase-preserve-onto.sh [new file with mode: 0755]
t/t3505-cherry-pick-empty.sh
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t3702-add-edit.sh [new file with mode: 0755]
t/t3900-i18n-commit.sh
t/t3900/ISO8859-1.txt [moved from t/t3900/ISO-8859-1.txt with 100% similarity]
t/t3900/eucJP.txt [moved from t/t3900/EUCJP.txt with 100% similarity]
t/t3901-i18n-patch.sh
t/t3903-stash.sh
t/t3904-stash-patch.sh [new file with mode: 0755]
t/t4007-rename-3.sh
t/t4013-diff-various.sh
t/t4013/diff.log_--decorate=full_--all [new file with mode: 0644]
t/t4013/diff.log_--decorate_--all
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4019-diff-wserror.sh
t/t4020-diff-external.sh
t/t4021-format-patch-numbered.sh
t/t4037-diff-r-t-dirs.sh [new file with mode: 0755]
t/t4038-diff-combined.sh [new file with mode: 0755]
t/t4039-diff-assume-unchanged.sh [new file with mode: 0755]
t/t4107-apply-ignore-whitespace.sh [new file with mode: 0755]
t/t4124-apply-ws-rule.sh
t/t4131-apply-fake-ancestor.sh [new file with mode: 0755]
t/t4132-apply-removal.sh [new file with mode: 0755]
t/t4150-am.sh
t/t4202-log.sh
t/t5000-tar-tree.sh
t/t5100-mailinfo.sh
t/t5100/info0014 [new file with mode: 0644]
t/t5100/info0014--scissors [new file with mode: 0644]
t/t5100/msg0014 [new file with mode: 0644]
t/t5100/msg0014--scissors [new file with mode: 0644]
t/t5100/patch0014 [new file with mode: 0644]
t/t5100/patch0014--scissors [new file with mode: 0644]
t/t5100/rfc2047-samples.mbox
t/t5100/sample.mbox
t/t5303-pack-corruption-resilience.sh
t/t5304-prune.sh
t/t5500-fetch-pack.sh
t/t5501-post-upload-pack.sh [new file with mode: 0755]
t/t5502-quickfetch.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t5520-pull.sh
t/t5530-upload-pack-error.sh
t/t5531-deep-submodule-push.sh [new file with mode: 0755]
t/t5540-http-push.sh
t/t5550-http-fetch.sh
t/t5601-clone.sh
t/t5706-clone-branch.sh [new file with mode: 0755]
t/t6010-merge-base.sh
t/t6015-rev-list-show-all-parents.sh [new file with mode: 0755]
t/t6016-rev-list-graph-simplify-history.sh [new file with mode: 0755]
t/t6020-merge-df.sh
t/t6023-merge-file.sh
t/t6024-recursive-merge.sh
t/t6030-bisect-porcelain.sh
t/t6035-merge-dir-to-symlink.sh [new file with mode: 0755]
t/t6036-recursive-corner-cases.sh [new file with mode: 0755]
t/t6040-tracking-info.sh
t/t6050-replace.sh [new file with mode: 0755]
t/t6200-fmt-merge-msg.sh
t/t7002-grep.sh
t/t7060-wtstatus.sh [new file with mode: 0755]
t/t7102-reset.sh
t/t7105-reset-patch.sh [new file with mode: 0755]
t/t7201-co.sh
t/t7300-clean.sh
t/t7401-submodule-summary.sh
t/t7406-submodule-update.sh [new file with mode: 0755]
t/t7407-submodule-foreach.sh [new file with mode: 0755]
t/t7408-submodule-reference.sh [new file with mode: 0755]
t/t7500-commit.sh
t/t7608-merge-messages.sh [new file with mode: 0755]
t/t7700-repack.sh
t/t8003-blame.sh
t/t8005-blame-i18n.sh
t/t8005/cp1251.txt [deleted file]
t/t8005/euc-japan.txt [new file with mode: 0644]
t/t8005/sjis.txt
t/t8005/utf8.txt
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/t9135-git-svn-moved-branch-empty-file.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/t9140-git-svn-reset.sh [new file with mode: 0755]
t/t9141-git-svn-multiple-branches.sh [new file with mode: 0755]
t/t9142-git-svn-shallow-clone.sh [new file with mode: 0755]
t/t9143-git-svn-gc.sh [new file with mode: 0755]
t/t9144-git-svn-old-rev_map.sh [new file with mode: 0755]
t/t9145-git-svn-master-branch.sh [new file with mode: 0755]
t/t9200-git-cvsexportcommit.sh
t/t9301-fast-export.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9501-gitweb-standalone-http-status.sh [new file with mode: 0755]
t/t9600-cvsimport.sh
t/t9601-cvsimport-vendor-branch.sh [new file with mode: 0755]
t/t9601/cvsroot/.gitattributes [new file with mode: 0644]
t/t9601/cvsroot/CVSROOT/.gitignore [new file with mode: 0644]
t/t9601/cvsroot/module/added-imported.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-anonymously.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-modified-imported.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-modified.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-once.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-twice.txt,v [new file with mode: 0644]
t/t9602-cvsimport-branches-tags.sh [new file with mode: 0755]
t/t9602/README [new file with mode: 0644]
t/t9602/cvsroot/.gitattributes [new file with mode: 0644]
t/t9602/cvsroot/CVSROOT/.gitignore [new file with mode: 0644]
t/t9602/cvsroot/module/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub1/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub1/subsubA/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub1/subsubB/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub2/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub2/subsubA/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub3/default,v [new file with mode: 0644]
t/t9603-cvsimport-patchsets.sh [new file with mode: 0755]
t/t9603/cvsroot/.gitattributes [new file with mode: 0644]
t/t9603/cvsroot/CVSROOT/.gitignore [new file with mode: 0644]
t/t9603/cvsroot/module/a,v [new file with mode: 0644]
t/t9603/cvsroot/module/b,v [new file with mode: 0644]
t/t9700-perl-git.sh
t/t9700/test.pl
t/test-lib.sh
templates/hooks--post-receive.sample
templates/hooks--pre-commit.sample
templates/hooks--update.sample
test-chmtime.c
test-date.c
test-delta.c
test-genrandom.c
test-parse-options.c
test-sha1.c
thread-utils.c
transport-helper.c [new file with mode: 0644]
transport.c
transport.h
tree-diff.c
unpack-file.c
unpack-trees.c
unpack-trees.h
update-server-info.c [deleted file]
upload-pack.c
usage.c
userdiff.c
utf8.c
var.c
walker.c
wrapper.c
write_or_die.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
xdiff/xdiffi.c
xdiff/xmerge.c
xdiff/xutils.c

index 6b9c715d21d5486e59083fb6071566aa6ecd4d42..0636deea9357d2f1e9331119f02fb75fb6b15393 100644 (file)
@@ -1,2 +1,2 @@
 * whitespace=!indent,trail,space
-*.[ch] whitespace
+*.[ch] whitespace=indent,trail,space
index 41c0b20a76ce0fd47c7cafc51777336ce99ce1b0..51a37b1af7ab927b98e2764e755b130080bfc9e3 100644 (file)
@@ -104,7 +104,9 @@ git-receive-pack
 git-reflog
 git-relink
 git-remote
+git-remote-curl
 git-repack
+git-replace
 git-repo-config
 git-request-pull
 git-rerere
@@ -177,3 +179,14 @@ configure
 tags
 TAGS
 cscope*
+*.obj
+*.lib
+*.sln
+*.suo
+*.ncb
+*.vcproj
+*.user
+*.idb
+*.pdb
+Debug/
+Release/
index 373476bdc03f718b4c01471dd9996ee4497f43a8..975e6758efa85c674207ffd6b400e3bbab2576a2 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -41,6 +41,7 @@ Michele Ballabio <barra_cuda@katamail.com>
 Nanako Shiraishi <nanako3@bluebottle.com>
 Nanako Shiraishi <nanako3@lavabit.com>
 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
+<nico@fluxnic.net> <nico@cam.org>
 Philippe Bruhat <book@cpan.org>
 Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
 René Scharfe <rene.scharfe@lsrfire.ath.cx>
index 7a8037f586773c00004e34079b48bb514db2515f..06b0c57b95057b3df4f215f3191fd8e9fc473e6f 100644 (file)
@@ -84,7 +84,7 @@ endif
 #
 
 ifdef ASCIIDOC8
-ASCIIDOC_EXTRA += -a asciidoc7compatible
+ASCIIDOC_EXTRA += -a asciidoc7compatible -a no-inline-literal
 endif
 ifdef DOCBOOK_XSL_172
 ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
diff --git a/Documentation/RelNotes-1.6.3.1.txt b/Documentation/RelNotes-1.6.3.1.txt
new file mode 100644 (file)
index 0000000..2400b72
--- /dev/null
@@ -0,0 +1,10 @@
+GIT v1.6.3.1 Release Notes
+==========================
+
+Fixes since v1.6.3
+------------------
+
+* "git checkout -b new-branch" with a staged change in the index
+  incorrectly primed the in-index cache-tree, resulting a wrong tree
+  object to be written out of the index.  This is a grave regression
+  since the last 1.6.2.X maintenance release.
diff --git a/Documentation/RelNotes-1.6.3.2.txt b/Documentation/RelNotes-1.6.3.2.txt
new file mode 100644 (file)
index 0000000..b2f3f02
--- /dev/null
@@ -0,0 +1,61 @@
+GIT v1.6.3.2 Release Notes
+==========================
+
+Fixes since v1.6.3.1
+--------------------
+
+ * A few codepaths picked up the first few bytes from an sha1[] by
+   casting the (char *) pointer to (int *); GCC 4.4 did not like this,
+   and aborted compilation.
+
+ * Some unlink(2) failures went undiagnosed.
+
+ * The "recursive" merge strategy misbehaved when faced rename/delete
+   conflicts while coming up with an intermediate merge base.
+
+ * The low-level merge algorithm did not handle a degenerate case of
+   merging a file with itself using itself as the common ancestor
+   gracefully.  It should produce the file itself, but instead
+   produced an empty result.
+
+ * GIT_TRACE mechanism segfaulted when tracing a shell-quoted aliases.
+
+ * OpenBSD also uses st_ctimspec in "struct stat", instead of "st_ctim".
+
+ * With NO_CROSS_DIRECTORY_HARDLINKS, "make install" can be told not to
+   create hardlinks between $(gitexecdir)/git-$builtin_commands and
+   $(bindir)/git.
+
+ * command completion code in bash did not reliably detect that we are
+   in a bare repository.
+
+ * "git add ." in an empty directory complained that pathspec "." did not
+   match anything, which may be technically correct, but not useful.  We
+   silently make it a no-op now.
+
+ * "git add -p" (and "patch" action in "git add -i") was broken when
+   the first hunk that adds a line at the top was split into two and
+   both halves are marked to be used.
+
+ * "git blame path" misbehaved at the commit where path became file
+   from a directory with some files in it.
+
+ * "git for-each-ref" had a segfaulting bug when dealing with a tag object
+   created by an ancient git.
+
+ * "git format-patch -k" still added patch numbers if format.numbered
+   configuration was set.
+
+ * "git grep --color ''" did not terminate.  The command also had
+   subtle bugs with its -w option.
+
+ * http-push had a small use-after-free bug.
+
+ * "git push" was converting OFS_DELTA pack representation into less
+   efficient REF_DELTA representation unconditionally upon transfer,
+   making the transferred data unnecessarily larger.
+
+ * "git remote show origin" segfaulted when origin was still empty.
+
+Many other general usability updates around help text, diagnostic messages
+and documentation are included as well.
diff --git a/Documentation/RelNotes-1.6.3.3.txt b/Documentation/RelNotes-1.6.3.3.txt
new file mode 100644 (file)
index 0000000..1c28398
--- /dev/null
@@ -0,0 +1,38 @@
+GIT v1.6.3.3 Release Notes
+==========================
+
+Fixes since v1.6.3.2
+--------------------
+
+ * "git archive" running on Cygwin can get stuck in an infinite loop.
+
+ * "git daemon" did not correctly parse the initial line that carries
+   virtual host request information.
+
+ * "git diff --textconv" leaked memory badly when the textconv filter
+   errored out.
+
+ * The built-in regular expressions to pick function names to put on
+   hunk header lines for java and objc were very inefficiently written.
+
+ * in certain error situations git-fetch (and git-clone) on Windows didn't
+   detect connection abort and ended up waiting indefinitely.
+
+ * import-tars script (in contrib) did not import symbolic links correctly.
+
+ * http.c used CURLOPT_SSLKEY even on libcURL version 7.9.2, even though
+   it was only available starting 7.9.3.
+
+ * low-level filelevel merge driver used return value from strdup()
+   without checking if we ran out of memory.
+
+ * "git rebase -i" left stray closing parenthesis in its reflog message.
+
+ * "git remote show" did not show all the URLs associated with the named
+   remote, even though "git remote -v" did.  Made them consistent by
+   making the former show all URLs.
+
+ * "whitespace" attribute that is set was meant to detect all errors known
+   to git, but it told git to ignore trailing carriage-returns.
+
+Includes other documentation fixes.
diff --git a/Documentation/RelNotes-1.6.3.4.txt b/Documentation/RelNotes-1.6.3.4.txt
new file mode 100644 (file)
index 0000000..cad461b
--- /dev/null
@@ -0,0 +1,36 @@
+GIT v1.6.3.4 Release Notes
+==========================
+
+Fixes since v1.6.3.3
+--------------------
+
+ * "git add --no-ignore-errors" did not override configured
+   add.ignore-errors configuration.
+
+ * "git apply --whitespace=fix" did not fix trailing whitespace on an
+   incomplete line.
+
+ * "git branch" opened too many commit objects unnecessarily.
+
+ * "git checkout -f $commit" with a path that is a file (or a symlink) in
+   the work tree to a commit that has a directory at the path issued an
+   unnecessary error message.
+
+ * "git diff -c/--cc" was very inefficient in coalescing the removed lines
+   shared between parents.
+
+ * "git diff -c/--cc" showed removed lines at the beginning of a file
+   incorrectly.
+
+ * "git remote show nickname" did not honor configured
+   remote.nickname.uploadpack when inspecting the branches at the remote.
+
+ * "git request-pull" when talking to the terminal for a preview
+   showed some of the output in the pager.
+
+ * "git request-pull start nickname [end]" did not honor configured
+   remote.nickname.uploadpack when it ran git-ls-remote against the remote
+   repository to learn the current tip of branches.
+
+Includes other documentation updates and minor fixes.
+
diff --git a/Documentation/RelNotes-1.6.4.1.txt b/Documentation/RelNotes-1.6.4.1.txt
new file mode 100644 (file)
index 0000000..e439e45
--- /dev/null
@@ -0,0 +1,46 @@
+GIT v1.6.4.1 Release Notes
+==========================
+
+Fixes since v1.6.4
+------------------
+
+ * An unquoted value in the configuration file, when it contains more than
+   one whitespaces in a row, got them replaced with a single space.
+
+ * "git am" used to accept a single piece of e-mail per file (not a mbox)
+   as its input, but multiple input format support in v1.6.4 broke it.
+   Apparently many people have been depending on this feature.
+
+ * The short help text for "git filter-branch" command was a single long
+   line, wrapped by terminals, and was hard to read.
+
+ * The "recursive" strategy of "git merge" segfaulted when a merge has
+   more than one merge-bases, and merging of these merge-bases involves
+   a rename/rename or a rename/add conflict.
+
+ * "git pull --rebase" did not use the right fork point when the
+   repository has already fetched from the upstream that rewinds the
+   branch it is based on in an earlier fetch.
+
+ * Explain the concept of fast-forward more fully in "git push"
+   documentation, and hint to refer to it from an error message when the
+   command refuses an update to protect the user.
+
+ * The default value for pack.deltacachesize, used by "git repack", is now
+   256M, instead of unbounded.  Otherwise a repack of a moderately sized
+   repository would needlessly eat into swap.
+
+ * Document how "git repack" (hence "git gc") interacts with a repository
+   that borrows its objects from other repositories (e.g. ones created by
+   "git clone -s").
+
+ * "git show" on an annotated tag lacked a delimiting blank line between
+   the tag itself and the contents of the object it tags.
+
+ * "git verify-pack -v" erroneously reported number of objects with too
+   deep delta depths as "chain length 0" objects.
+
+ * Long names of authors and committers outside US-ASCII were sometimes
+   incorrectly shown in "gitweb".
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.4.2.txt b/Documentation/RelNotes-1.6.4.2.txt
new file mode 100644 (file)
index 0000000..c11ec01
--- /dev/null
@@ -0,0 +1,32 @@
+GIT v1.6.4.2 Release Notes
+==========================
+
+Fixes since v1.6.4.1
+--------------------
+
+* --date=relative output between 1 and 5 years ago rounded the number of
+    years when saying X years Y months ago, instead of rounding it down.
+
+* "git add -p" did not handle changes in executable bits correctly
+  (a regression around 1.6.3).
+
+* "git apply" did not honor GNU diff's convention to mark the creation/deletion
+  event with UNIX epoch timestamp on missing side.
+
+* "git checkout" incorrectly removed files in a directory pointed by a
+  symbolic link during a branch switch that replaces a directory with
+  a symbolic link.
+
+* "git clean -d -f" happily descended into a subdirectory that is managed by a
+  separate git repository.  It now requires two -f options for safety.
+
+* "git fetch/push" over http transports had two rather grave bugs.
+
+* "git format-patch --cover-letter" did not prepare the cover letter file
+  for use with non-ASCII strings when there are the series contributors with
+  non-ASCII names.
+
+* "git pull origin branch" and "git fetch origin && git merge origin/branch"
+  left different merge messages in the resulting commit.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.4.3.txt b/Documentation/RelNotes-1.6.4.3.txt
new file mode 100644 (file)
index 0000000..4f29bab
--- /dev/null
@@ -0,0 +1,29 @@
+GIT v1.6.4.3 Release Notes
+==========================
+
+Fixes since v1.6.4.2
+--------------------
+
+* "git clone" from an empty repository gave unnecessary error message,
+  even though it did everything else correctly.
+
+* "git cvsserver" invoked git commands via "git-foo" style, which has long
+  been deprecated.
+
+* "git fetch" and "git clone" had an extra sanity check to verify the
+  presense of the corresponding *.pack file before downloading *.idx
+  file by issuing a HEAD request.  Github server however sometimes
+  gave 500 (Internal server error) response to HEAD even if a GET
+  request for *.pack file to the same URL would have succeeded, and broke
+  clone over HTTP from some of their repositories.  As a workaround, this
+  verification has been removed (as it is not absolutely necessary).
+
+* "git grep" did not like relative pathname to refer outside the current
+  directory when run from a subdirectory.
+
+* an error message from "git push" was formatted in a very ugly way.
+
+* "git svn" did not quote the subversion user name correctly when
+  running its author-prog helper program.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.4.4.txt b/Documentation/RelNotes-1.6.4.4.txt
new file mode 100644 (file)
index 0000000..0ead45f
--- /dev/null
@@ -0,0 +1,26 @@
+GIT v1.6.4.4 Release Notes
+==========================
+
+Fixes since v1.6.4.4
+--------------------
+
+* The workaround for Github server that sometimes gave 500 (Internal server
+  error) response to HEAD requests in 1.6.4.3 introduced a regression that
+  caused re-fetching projects over http to segfault in certain cases due
+  to uninitialized pointer being freed.
+
+* "git pull" on an unborn branch used to consider anything in the work
+  tree and the index discardable.
+
+* "git diff -b/w" did not work well on the incomplete line at the end of
+  the file, due to an incorrect hashing of lines in the low-level xdiff
+  routines.
+
+* "git checkout-index --prefix=$somewhere" used to work when $somewhere is
+  a symbolic link to a directory elsewhere, but v1.6.4.2 broke it.
+
+* "git unpack-objects --strict", invoked when receive.fsckobjects
+  configuration is set in the receiving repository of "git push", did not
+  properly check the objects, especially the submodule links, it received.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.4.txt b/Documentation/RelNotes-1.6.4.txt
new file mode 100644 (file)
index 0000000..7a90441
--- /dev/null
@@ -0,0 +1,147 @@
+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.
+
+
+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, 'git svn reset' to unwind
+   'git svn fetch', support for more than one branches, documenting
+   of the useful --minimize-url feature, new "git svn gc" command, etc.
+
+(portability)
+
+ * We feed iconv with "UTF-8" instead of "utf8"; the former is
+   understood more widely.  Similarly updated test scripts to use
+   encoding names more widely understood (e.g. use "ISO8859-1" instead
+   of "ISO-8859-1").
+
+ * Various portability fixes/workarounds for different vintages of
+   SunOS, IRIX, and Windows.
+
+ * Git-over-ssh transport on Windows supports PuTTY plink and TortoisePlink.
+
+(performance)
+
+ * Many repeated use of lstat() are optimized out in "checkout" codepath.
+
+ * git-status (and underlying git-diff-index --cached) are optimized
+   to take advantage of cache-tree information in the index.
+
+(usability, bells and whistles)
+
+ * "git add --edit" lets users edit the whole patch text to fine-tune what
+   is added to the index.
+
+ * "git am" accepts StGIT series file as its input.
+
+ * "git bisect skip" skips to a more randomly chosen place in the hope
+   to avoid testing a commit that is too close to a commit that is
+   already known to be untestable.
+
+ * "git cvsexportcommit" learned -k option to stop CVS keywords expansion
+
+ * "git fast-export" learned to handle history simplification more
+   gracefully.
+
+ * "git fast-export" learned an option --tag-of-filtered-object to handle
+   dangling tags resulting from history simplification more usefully.
+
+ * "git grep" learned -p option to show the location of the match using the
+   same context hunk marker "git diff" uses.
+
+ * https transport can optionally be told that the used client
+   certificate is password protected, in which case it asks the
+   password only once.
+
+ * "git imap-send" is IPv6 aware.
+
+ * "git log --graph" draws graphs more compactly by using horizontal lines
+   when able.
+
+ * "git log --decorate" shows shorter refnames by stripping well-known
+   refs/* prefix.
+
+ * "git push $name" honors remote.$name.pushurl if present before
+   using remote.$name.url.  In other words, the URL used for fetching
+   and pushing can be different.
+
+ * "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.
+
+ * "git submodule update" learned --rebase option to update checked
+   out submodules by rebasing the local changes.
+
+ * "gitweb" can optionally use gravatar to adorn author/committer names.
+
+(developers)
+
+ * A major part of the "git bisect" wrapper has moved to C.
+
+ * Formatting with the new version of AsciiDoc 8.4.1 is now supported.
+
+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.
+
+ * "git diff-tree -r -t" used to omit new or removed directories from
+   the output.  df533f3 (diff-tree -r -t: include added/removed
+   directories in the output, 2009-06-13) may need to be cherry-picked
+   to backport this fix.
+
+ * 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.
+
+ * Removing a section with "git config --remove-section", when its
+   section header has a variable definition on the same line, lost
+   that variable definition.
+
+ * "git rebase -p --onto" used to always leave side branches of a merge
+   intact, even when both branches are subject to rewriting.
+
+ * "git repack" used to faithfully follow grafts and considered true
+   parents recorded in the commit object unreachable from the commit.
+   After such a repacking, you cannot remove grafts without corrupting
+   the repository.
+
+ * "git send-email" did not detect erroneous loops in alias expansion.
diff --git a/Documentation/RelNotes-1.6.5.1.txt b/Documentation/RelNotes-1.6.5.1.txt
new file mode 100644 (file)
index 0000000..309ba18
--- /dev/null
@@ -0,0 +1,20 @@
+GIT v1.6.5.1 Release Notes
+==========================
+
+Fixes since v1.6.5
+------------------
+
+ * An corrupt pack could make codepath to read objects into an
+   infinite loop.
+
+ * Download throughput display was always shown in KiB/s but on fast links
+   it is more appropriate to show it in MiB/s.
+
+ * "git grep -f filename" used uninitialized variable and segfaulted.
+
+ * "git clone -b branch" gave a wrong commit object name to post-checkout
+   hook.
+
+ * "git pull" over http did not work on msys.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.5.txt b/Documentation/RelNotes-1.6.5.txt
new file mode 100644 (file)
index 0000000..ee141c1
--- /dev/null
@@ -0,0 +1,169 @@
+GIT v1.6.5 Release Notes
+========================
+
+In git 1.7.0, which was planned to be the release after 1.6.5, "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.
+
+Also, "git push $there :$killed" to delete the branch $killed in a remote
+repository $there, when $killed branch is the current branch pointed at by
+its HEAD, will be refused by default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyDeleteCurrent 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.
+
+Updates since v1.6.4
+--------------------
+
+(subsystems)
+
+ * various updates to gitk, git-svn and gitweb.
+
+(portability)
+
+ * more improvements on mingw port.
+
+ * mingw will also give FRSX as the default value for the LESS
+   environment variable when the user does not have one.
+
+ * initial support to compile git on Windows with MSVC.
+
+(performance)
+
+ * On major platforms, the system can be compiled to use with Linus's
+   block-sha1 implementation of the SHA-1 hash algorithm, which
+   outperforms the default fallback implementation we borrowed from
+   Mozilla.
+
+ * Unnecessary inefficiency in deepening of a shallow repository has
+   been removed.
+
+ * "git clone" does not grab objects that it does not need (i.e.
+   referenced only from refs outside refs/heads and refs/tags
+   hierarchy) anymore.
+
+ * The "git" main binary used to link with libcurl, which then dragged
+   in a large number of external libraries.  When using basic plumbing
+   commands in scripts, this unnecessarily slowed things down.  We now
+   implement http/https/ftp transfer as a separate executable as we
+   used to.
+
+ * "git clone" run locally hardlinks or copies the files in .git/ to
+   newly created repository.  It used to give new mtime to copied files,
+   but this delayed garbage collection to trigger unnecessarily in the
+   cloned repository.  We now preserve mtime for these files to avoid
+   this issue.
+
+(usability, bells and whistles)
+
+ * Human writable date format to various options, e.g. --since=yesterday,
+   master@{2000.09.17}, are taught to infer some omitted input properly.
+
+ * A few programs gave verbose "advice" messages to help uninitiated
+   people when issuing error messages.  An infrastructure to allow
+   users to squelch them has been introduced, and a few such messages
+   can be silenced now.
+
+ * refs/replace/ hierarchy is designed to be usable as a replacement
+   of the "grafts" mechanism, with the added advantage that it can be
+   transferred across repositories.
+
+ * "git am" learned to optionally ignore whitespace differences.
+
+ * "git am" handles input e-mail files that has CRLF line endings sensibly.
+
+ * "git am" learned "--scissors" option to allow you to discard early part
+   of an incoming e-mail.
+
+ * "git archive -o output.zip" works without being told what format to
+   use with an explicit "--format=zip".option.
+
+ * "git checkout", "git reset" and "git stash" learned to pick and
+   choose to use selected changes you made, similar to "git add -p".
+
+ * "git clone" learned a "-b" option to pick a HEAD to check out
+   different from the remote's default branch.
+
+ * "git clone" learned --recursive option.
+
+ * "git clone" from a local repository on a different filesystem used to
+   copy individual object files without preserving the old timestamp, giving
+   them extra lifetime in the new repository until they gc'ed.
+
+ * "git commit --dry-run $args" is a new recommended way to ask "what would
+   happen if I try to commit with these arguments."
+
+ * "git commit --dry-run" and "git status" shows conflicted paths in a
+   separate section to make them easier to spot during a merge.
+
+ * "git cvsimport" now supports password-protected pserver access even
+   when the password is not taken from ~/.cvspass file.
+
+ * "git fast-export" learned --no-data option that can be useful when
+   reordering commits and trees without touching the contents of
+   blobs.
+
+ * "git fast-import" has a pair of new front-end in contrib/ area.
+
+ * "git init" learned to mkdir/chdir into a directory when given an
+   extra argument (i.e. "git init this").
+
+ * "git instaweb" optionally can use mongoose as the web server.
+
+ * "git log --decorate" can optionally be told with --decorate=full to
+   give the reference name in full.
+
+ * "git merge" issued an unnecessarily scary message when it detected
+   that the merge may have to touch the path that the user has local
+   uncommitted changes to. The message has been reworded to make it
+   clear that the command aborted, without doing any harm.
+
+ * "git push" can be told to be --quiet.
+
+ * "git push" pays attention to url.$base.pushInsteadOf and uses a URL
+   that is derived from the URL used for fetching.
+
+ * informational output from "git reset" that lists the locally modified
+   paths is made consistent with that of "git checkout $another_branch".
+
+ * "git submodule" learned to give submodule name to scripts run with
+   "foreach" subcommand.
+
+ * various subcommands to "git submodule" learned --recursive option.
+
+ * "git submodule summary" learned --files option to compare the work
+   tree vs the commit bound at submodule path, instead of comparing
+   the index.
+
+ * "git upload-pack", which is the server side support for "git clone" and
+   "git fetch", can call a new post-upload-pack hook for statistics purposes.
+
+(developers)
+
+ * With GIT_TEST_OPTS="--root=/p/a/t/h", tests can be run outside the
+   source directory; using tmpfs may give faster turnaround.
+
+ * With NO_PERL_MAKEMAKER set, DESTDIR= is now honoured, so you can
+   build for one location, and install into another location to tar it
+   up.
+
+Fixes since v1.6.4
+------------------
+
+All of the fixes in v1.6.4.X maintenance series are included in this
+release, unless otherwise noted.
diff --git a/Documentation/RelNotes-1.6.6.txt b/Documentation/RelNotes-1.6.6.txt
new file mode 100644 (file)
index 0000000..5f1fecb
--- /dev/null
@@ -0,0 +1,60 @@
+GIT v1.6.6 Release Notes
+========================
+
+In git 1.7.0, which is planned to be the release after 1.6.6, "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.
+
+Also, "git push $there :$killed" to delete the branch $killed in a remote
+repository $there, when $killed branch is the current branch pointed at by
+its HEAD, will be refused by default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyDeleteCurrent 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.
+
+Updates since v1.6.5
+--------------------
+
+(subsystems)
+
+(portability)
+
+(performance)
+
+(usability, bells and whistles)
+
+ * "git log --decorate" shows the location of HEAD as well.
+
+(developers)
+
+Fixes since v1.6.5
+------------------
+
+All of the fixes in v1.6.5.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git apply" and "git diff" (including patch output from "git log -p")
+   now flags trailing blank lines as whitespace errors correctly (only
+   "apply --whitespace=fix" stripped them but "apply --whitespace=warn"
+   did not even warn).
+
+ * Two whitespace error classes, 'blank-at-eof' and 'blank-at-eol', have
+   been introduced (settable by core.whitespace configuration variable and
+   whitespace attribute).  The 'trailing-space' whitespace error class has
+   become a short-hand to cover both of these and there is no behaviour
+   change for existing set-ups.
index dc76e7f073f23cd911da0b0d0d49b72726576f1a..87a90f2c3fd48bbb593a2cef135310f88d87e4b4 100644 (file)
@@ -17,6 +17,7 @@ caret=&#94;
 startsb=&#91;
 endsb=&#93;
 tilde=&#126;
+backtick=&#96;
 
 ifdef::backend-docbook[]
 [linkgit-inlinemacro]
index 5dcad94f841c395beb21961ebdacd341d76b25c9..5a8f57cbf77b5aa1b84c9d422f54880ba473c576 100644 (file)
@@ -49,7 +49,8 @@ There is also a case insensitive alternative `[section.subsection]` syntax.
 In this syntax, subsection names follow the same restrictions as for section
 names.
 
-All the other lines are recognized as setting variables, in the form
+All the other lines (and the remainder of the line after the section
+header) are recognized as setting variables, in the form
 'name = value'.  If there is no equal sign on the line, the entire line
 is taken as 'name' and the variable is recognized as boolean "true".
 The variable names are case-insensitive and only alphanumeric
@@ -112,6 +113,21 @@ For command-specific variables, you will find a more detailed description
 in the appropriate manual page. You will find a description of non-core
 porcelain configuration variables in the respective porcelain documentation.
 
+advice.*::
+       When set to 'true', display the given optional help message.
+       When set to 'false', do not display. The configuration variables
+       are:
++
+--
+       pushNonFastForward::
+               Advice shown when linkgit:git-push[1] refuses
+               non-fast-forward refs. Default: true.
+       statusHints::
+               Directions on how to stage/unstage/add shown in the
+               output of linkgit:git-status[1] and the template shown
+               when writing commit messages. Default: true.
+--
+
 core.fileMode::
        If false, the executable bit differences between the index and
        the working copy are ignored; useful on broken filesystems like FAT.
@@ -400,13 +416,17 @@ core.whitespace::
        consider them as errors.  You can prefix `-` to disable
        any of them (e.g. `-trailing-space`):
 +
-* `trailing-space` treats trailing whitespaces at the end of the line
+* `blank-at-eol` treats trailing whitespaces at the end of the line
   as an error (enabled by default).
 * `space-before-tab` treats a space character that appears immediately
   before a tab character in the initial indent part of the line as an
   error (enabled by default).
 * `indent-with-non-tab` treats a line that is indented with 8 or more
   space characters as an error (not enabled by default).
+* `blank-at-eof` treats blank lines added at the end of file as an error
+  (enabled by default).
+* `trailing-space` is a short-hand to cover both `blank-at-eol` and
+  `blank-at-eof`.
 * `cr-at-eol` treats a carriage-return at the end of line as
   part of the line terminator, i.e. with it, `trailing-space`
   does not trigger if the character before such a carriage-return
@@ -438,6 +458,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
@@ -451,7 +476,17 @@ If the alias expansion is prefixed with an exclamation point,
 it will be treated as a shell command.  For example, defining
 "alias.new = !gitk --all --not ORIG_HEAD", the invocation
 "git new" is equivalent to running the shell command
-"gitk --all --not ORIG_HEAD".
+"gitk --all --not ORIG_HEAD".  Note that shell commands will be
+executed from the top-level directory of a repository, which may
+not necessarily be the current directory.
+
+apply.ignorewhitespace::
+       When set to 'change', tells 'git-apply' to ignore changes in
+       whitespace, in the same way as the '--ignore-space-change'
+       option.
+       When set to one of: no, none, never, false tells 'git-apply' to
+       respect all whitespace differences.
+       See linkgit:git-apply[1].
 
 apply.whitespace::
        Tells 'git-apply' how to handle whitespaces, in the same way
@@ -508,7 +543,7 @@ branch.<name>.merge::
 
 branch.<name>.mergeoptions::
        Sets default options for merging into branch <name>. The syntax and
-       supported options are equal to that of linkgit:git-merge[1], but
+       supported options are the same as those of linkgit:git-merge[1], but
        option values containing whitespace characters are currently not
        supported.
 
@@ -597,13 +632,19 @@ color.interactive.<slot>::
        Use customized color for 'git-add --interactive'
        output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
        four distinct types of normal output from interactive
-       programs.  The values of these variables may be specified as
+       commands.  The values of these variables may be specified as
        in color.branch.<slot>.
 
 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`,
@@ -1032,6 +1073,12 @@ http.sslKey::
        over HTTPS. Can be overridden by the 'GIT_SSL_KEY' environment
        variable.
 
+http.sslCertPasswordProtected::
+       Enable git's password prompt for the SSL certificate.  Otherwise
+       OpenSSL will prompt the user, possibly many times, if the
+       certificate or private key is encrypted.  Can be overridden by the
+       'GIT_SSL_CERT_PASSWORD_PROTECTED' environment variable.
+
 http.sslCAInfo::
        File containing the certificates to verify the peer with when
        fetching or pushing over HTTPS. Can be overridden by the
@@ -1093,7 +1140,7 @@ instaweb.port::
        linkgit:git-instaweb[1].
 
 interactive.singlekey::
-       In interactive programs, allow the user to provide one-letter
+       In interactive commands, allow the user to provide one-letter
        input with a single key (i.e., without hitting enter).
        Currently this is used only by the `\--patch` mode of
        linkgit:git-add[1].  Note that this setting is silently
@@ -1198,12 +1245,20 @@ pack.compression::
 
 pack.deltaCacheSize::
        The maximum memory in bytes used for caching deltas in
-       linkgit:git-pack-objects[1].
-       A value of 0 means no limit. Defaults to 0.
+       linkgit:git-pack-objects[1] before writing them out to a pack.
+       This cache is used to speed up the writing object phase by not
+       having to recompute the final delta result once the best match
+       for all objects is found.  Repacking large repositories on machines
+       which are tight with memory might be badly impacted by this though,
+       especially if this cache pushes the system into swapping.
+       A value of 0 means no limit. The smallest size of 1 byte may be
+       used to virtually disable this cache. Defaults to 256 MiB.
 
 pack.deltaCacheLimit::
        The maximum size of a delta, that is cached in
-       linkgit:git-pack-objects[1]. Defaults to 1000.
+       linkgit:git-pack-objects[1]. This cache is used to speed up the
+       writing object phase by not having to recompute the final delta
+       result once the best match for all objects is found. Defaults to 1000.
 
 pack.threads::
        Specifies the number of threads to spawn when searching for best
@@ -1308,6 +1363,9 @@ remote.<name>.url::
        The URL of a remote repository.  See linkgit:git-fetch[1] or
        linkgit:git-push[1].
 
+remote.<name>.pushurl::
+       The push URL of a remote repository.  See linkgit:git-push[1].
+
 remote.<name>.proxy::
        For remotes that require curl (http, https and ftp), the URL to
        the proxy to use for that remote.  Set to the empty string to
@@ -1365,6 +1423,50 @@ rerere.enabled::
        default enabled if you create `rr-cache` directory under
        `$GIT_DIR`, but can be disabled by setting this option to false.
 
+sendemail.identity::
+       A configuration identity. When given, causes values in the
+       'sendemail.<identity>' subsection to take precedence over
+       values in the 'sendemail' section. The default identity is
+       the value of 'sendemail.identity'.
+
+sendemail.smtpencryption::
+       See linkgit:git-send-email[1] for description.  Note that this
+       setting is not subject to the 'identity' mechanism.
+
+sendemail.smtpssl::
+       Deprecated alias for 'sendemail.smtpencryption = ssl'.
+
+sendemail.<identity>.*::
+       Identity-specific versions of the 'sendemail.*' parameters
+       found below, taking precedence over those when the this
+       identity is selected, through command-line or
+       'sendemail.identity'.
+
+sendemail.aliasesfile::
+sendemail.aliasfiletype::
+sendemail.bcc::
+sendemail.cc::
+sendemail.cccmd::
+sendemail.chainreplyto::
+sendemail.confirm::
+sendemail.envelopesender::
+sendemail.from::
+sendemail.multiedit::
+sendemail.signedoffbycc::
+sendemail.smtppass::
+sendemail.suppresscc::
+sendemail.suppressfrom::
+sendemail.to::
+sendemail.smtpserver::
+sendemail.smtpserverport::
+sendemail.smtpuser::
+sendemail.thread::
+sendemail.validate::
+       See linkgit:git-send-email[1] for description.
+
+sendemail.signedoffcc::
+       Deprecated alias for 'sendemail.signedoffbycc'.
+
 showbranch.default::
        The default set of branches for linkgit:git-show-branch[1].
        See linkgit:git-show-branch[1].
@@ -1417,6 +1519,19 @@ url.<base>.insteadOf::
        never-before-seen repository on the site.  When more than one
        insteadOf strings match a given URL, the longest match is used.
 
+url.<base>.pushInsteadOf::
+       Any URL that starts with this value will not be pushed to;
+       instead, it will be rewritten to start with <base>, and the
+       resulting URL will be pushed to. In cases where some site serves
+       a large number of repositories, and serves them with multiple
+       access methods, some of which do not allow push, this feature
+       allows people to specify a pull-only URL and have git
+       automatically use an appropriate URL to push, even for a
+       never-before-seen repository on the site.  When more than one
+       pushInsteadOf strings match a given URL, the longest match is
+       used.  If a remote has an explicit pushurl, git will ignore this
+       setting for that remote.
+
 user.email::
        Your email address to be recorded in any newly created commits.
        Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
index 1eeb1c76838c1911fc4d57b36a16dece0538809a..b71712473ed13020bca3f133ea4a28c5081b7f9e 100644 (file)
@@ -1,4 +1,7 @@
-The output format from "git-diff-index", "git-diff-tree",
+Raw output format
+-----------------
+
+The raw output format from "git-diff-index", "git-diff-tree",
 "git-diff-files" and "git diff --raw" are very similar.
 
 These commands all compare two sets of things; what is
@@ -16,6 +19,9 @@ git-diff-tree [-r] <tree-ish-1> <tree-ish-2> [<pattern>...]::
 git-diff-files [<pattern>...]::
         compares the index and the files on the filesystem.
 
+The "git-diff-tree" command begins its ouput by printing the hash of
+what is being compared. After that, all the commands print one output
+line per changed file.
 
 An output line is formatted this way:
 
index d313795fdbc420e3395adc42aebe82fabda037d4..5eb2b0ee07b2f4e81e909a103f486c1a6d2dc6cb 100644 (file)
@@ -1,11 +1,13 @@
+ifndef::git-pull[]
 -q::
 --quiet::
        Pass --quiet to git-fetch-pack and silence any other internally
-       used programs.
+       used git commands.
 
 -v::
 --verbose::
        Be verbose.
+endif::git-pull[]
 
 -a::
 --append::
index d938b422893067feb1a430edb34fb51ef7db6d85..45ebf87ca3c31bce8645a0130625f4b653929e5c 100644 (file)
@@ -9,8 +9,8 @@ SYNOPSIS
 --------
 [verse]
 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
-         [--all | [--update | -u]] [--intent-to-add | -N]
-         [--refresh] [--ignore-errors] [--] <filepattern>...
+         [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
+         [--refresh] [--ignore-errors] [--] [<filepattern>...]
 
 DESCRIPTION
 -----------
@@ -72,9 +72,23 @@ OPTIONS
 
 -p::
 --patch::
-       Similar to Interactive mode but the initial command loop is
-       bypassed and the 'patch' subcommand is invoked using each of
-       the specified filepatterns before exiting.
+       Interactively choose hunks of patch between the index and the
+       work tree and add them to the index. This gives the user a chance
+       to review the difference before adding modified contents to the
+       index.
+
+       This effectively runs ``add --interactive``, but bypasses the
+       initial command menu and directly jumps to `patch` subcommand.
+       See ``Interactive mode'' for details.
+
+-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::
index 6d92cbee6475e29660d91a97683bf5843aacab38..67ad5da9cc0e766882300e90ed114306cb3c9fa7 100644 (file)
@@ -11,9 +11,9 @@ SYNOPSIS
 [verse]
 'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
         [--3way] [--interactive] [--committer-date-is-author-date]
-        [--ignore-date]
+        [--ignore-date] [--ignore-space-change | --ignore-whitespace]
         [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
-        [--reject]
+        [--reject] [-q | --quiet] [--scissors | --no-scissors]
         [<mbox> | <Maildir>...]
 'git am' (--skip | --resolved | --abort)
 
@@ -39,6 +39,18 @@ OPTIONS
 --keep::
        Pass `-k` flag to 'git-mailinfo' (see linkgit:git-mailinfo[1]).
 
+-c::
+--scissors::
+       Remove everything in body before a scissors line (see
+       linkgit:git-mailinfo[1]).
+
+---no-scissors::
+       Ignore scissors lines (see linkgit:git-mailinfo[1]).
+
+-q::
+--quiet::
+       Be quiet. Only print error messages.
+
 -u::
 --utf8::
        Pass `-u` flag to 'git-mailinfo' (see linkgit:git-mailinfo[1]).
@@ -61,6 +73,9 @@ default.   You can use `--no-utf8` to override this.
        it is supposed to apply to and we have those blobs
        available locally.
 
+--ignore-date::
+--ignore-space-change::
+--ignore-whitespace::
 --whitespace=<option>::
 -C<n>::
 -p<n>::
@@ -121,10 +136,8 @@ the commit, after stripping common prefix "[PATCH <anything>]".
 The "Subject: " line is supposed to concisely describe what the
 commit is about in one line of text.
 
-"From: " and "Subject: " lines starting the body (the rest of the
-message after the blank line terminating the RFC2822 headers)
-override the respective commit author name and title values taken
-from the headers.
+"From: " and "Subject: " lines starting the body override the respective
+commit author name and title values taken from the headers.
 
 The commit message is formed by the title taken from the
 "Subject: ", a blank line and the body of the message up to
index 9e5baa277731adf14e47fda8c6b13a076e0873db..5ee8c91f2d0a376bf755f0802825ae184cea2a28 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
@@ -13,6 +13,7 @@ SYNOPSIS
          [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
          [--allow-binary-replacement | --binary] [--reject] [-z]
          [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
+         [--ignore-space-change | --ignore-whitespace ]
          [--whitespace=<nowarn|warn|fix|error|error-all>]
          [--exclude=PATH] [--include=PATH] [--directory=<root>]
          [--verbose] [<patch>...]
@@ -149,6 +150,14 @@ patch to each path is used.  A patch to a path that does not match any
 include/exclude pattern is used by default if there is no include pattern
 on the command line, and ignored if there is any include pattern.
 
+--ignore-space-change::
+--ignore-whitespace::
+       When applying a patch, ignore changes in whitespace in context
+       lines if necessary.
+       Context lines will preserve their whitespace, and they will not
+       undergo whitespace fixing regardless of the value of the
+       `--whitespace` option. New lines will still be fixed, though.
+
 --whitespace=<action>::
        When applying a patch, detect a new or modified line that has
        whitespace errors.  What are considered whitespace errors is
@@ -205,6 +214,10 @@ running `git apply --directory=modules/git-gui`.
 Configuration
 -------------
 
+apply.ignorewhitespace::
+       Set to 'change' if you want changes in whitespace to be ignored by default.
+       Set to one of: no, none, never, false if you want changes in
+       whitespace to be significant.
 apply.whitespace::
        When no `--whitespace` flag is given from the command
        line, this configuration item is used as the default.
index bc132c87e1a5b9f42ea1f2bbd4e3c46f67fc3852..3d1c1e75b703cc7ba7013b4c8badb50f51966e1f 100644 (file)
@@ -9,8 +9,8 @@ git-archive - Create an archive of files from a named tree
 SYNOPSIS
 --------
 [verse]
-'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
-             [--output=<file>] [--worktree-attributes]
+'git archive' [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>]
+             [-o | --output=<file>] [--worktree-attributes]
              [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
              [path...]
 
@@ -34,8 +34,11 @@ OPTIONS
 -------
 
 --format=<fmt>::
-       Format of the resulting archive: 'tar' or 'zip'.  The default
-       is 'tar'.
+       Format of the resulting archive: 'tar' or 'zip'. If this option
+       is not given, and the output file is specified, the format is
+       inferred from the filename if possible (e.g. writing to "foo.zip"
+       makes the output to be in the zip format). Otherwise the output
+       format is `tar`.
 
 -l::
 --list::
@@ -48,6 +51,7 @@ OPTIONS
 --prefix=<prefix>/::
        Prepend <prefix>/ to each filename in the archive.
 
+-o <file>::
 --output=<file>::
        Write the archive to <file> instead of stdout.
 
@@ -129,6 +133,12 @@ git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs
        Put everything in the current head's Documentation/ directory
        into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
 
+git archive -o latest.zip HEAD::
+
+       Create a Zip archive that contains the contents of the latest
+       commit on the current branch. Note that the output format is
+       inferred by the extension of the output file.
+
 
 SEE ALSO
 --------
index ffc02c737cf0a8d2bdb3cd812e0a23d43ee27fd7..63e7a42cb353e2cd0c8bbb6e83927bd1863a32cb 100644 (file)
@@ -164,9 +164,8 @@ to do it for you by issuing the command:
 $ git bisect skip                 # Current version cannot be tested
 ------------
 
-But computing the commit to test may be slower afterwards and git may
-eventually not be able to tell the first bad commit among a bad commit
-and one or more skipped commits.
+But git may eventually be unable to tell the first bad commit among
+a bad commit and one or more skipped commits.
 
 You can even skip a range of commits, instead of just one commit,
 using the "'<commit1>'..'<commit2>'" notation. For example:
index cbd427587188d81726445183f772f02982736e6a..0e836809c20c83b8798fc77fa373f17fbf5f1eb6 100644 (file)
@@ -30,10 +30,8 @@ commit) will be listed.  With `--no-merged` only branches not merged into
 the named commit will be listed.  If the <commit> argument is missing it
 defaults to 'HEAD' (i.e. the tip of the current branch).
 
-In the command's second form, a new branch named <branchname> will be created.
-It will start out with a head equal to the one given as <start-point>.
-If no <start-point> is given, the branch will be created with a head
-equal to that of the currently checked out branch.
+The command's second form creates a new branch head named <branchname>
+which points to the current 'HEAD', or <start-point> if given.
 
 Note that this will create the new branch, but it will not switch the
 working tree to it; use "git checkout <newbranch>" to switch to the
@@ -76,6 +74,7 @@ OPTIONS
        based sha1 expressions such as "<branchname>@\{yesterday}".
 
 -f::
+--force::
        Reset <branchname> to <startpoint> if <branchname> exists
        already. Without `-f` 'git-branch' refuses to change an existing branch.
 
@@ -111,6 +110,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
@@ -132,11 +132,13 @@ start-point is either a local or remote branch.
 --contains <commit>::
        Only list branches which contain the specified commit.
 
---merged::
-       Only list branches which are fully contained by HEAD.
+--merged [<commit>]::
+       Only list branches whose tips are reachable from the
+       specified commit (HEAD if not specified).
 
---no-merged::
-       Do not list branches which are fully contained by HEAD.
+--no-merged [<commit>]::
+       Only list branches whose tips are not reachable from the
+       specified commit (HEAD if not specified).
 
 <branchname>::
        The name of the branch to create or delete.
@@ -145,9 +147,9 @@ start-point is either a local or remote branch.
        may restrict the characters allowed in a branch name.
 
 <start-point>::
-       The new branch will be created with a HEAD equal to this.  It may
-       be given as a branch name, a commit-id, or a tag.  If this option
-       is omitted, the current branch is assumed.
+       The new branch head will point to this commit.  It may be
+       given as a branch name, a commit-id, or a tag.  If this
+       option is omitted, the current HEAD will be used instead.
 
 <oldbranch>::
        The name of an existing branch to rename.
@@ -208,6 +210,14 @@ but different purposes:
 - `--no-merged` is used to find branches which are candidates for merging
   into HEAD, since those branches are not fully contained by HEAD.
 
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1],
+linkgit:git-fetch[1],
+linkgit:git-remote[1],
+link:user-manual.html#what-is-a-branch[``Understanding history: What is
+a branch?''] in the Git User's Manual.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org> and Junio C Hamano <gitster@pobox.com>
index b191276d7a44511bfce60febe6f5cb9a025ec9a3..58c8d65772af4ef20ad573af9dd28691f5357437 100644 (file)
@@ -9,8 +9,8 @@ git-cat-file - Provide content or type and size information for repository objec
 SYNOPSIS
 --------
 [verse]
-'git cat-file' [-t | -s | -e | -p | <type>] <object>
-'git cat-file' [--batch | --batch-check] < <list-of-objects>
+'git cat-file' (-t | -s | -e | -p | <type>) <object>
+'git cat-file' (--batch | --batch-check) < <list-of-objects>
 
 DESCRIPTION
 -----------
index 664da8a988170ab09d16e3e30953b2cf0ea6c3df..e9b3b40af497c7b8508e31e171bf87e565b96963 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 ad4b31e89218e857001fce69f32acbe85fc7539c..37c1810e3fc8424868333a22094107e99764fc37 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 'git checkout' [-q] [-f] [-m] [<branch>]
 'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
+'git checkout' --patch [<tree-ish>] [--] [<paths>...]
 
 DESCRIPTION
 -----------
@@ -25,7 +26,7 @@ use the --track or --no-track options, which will be passed to `git
 branch`.  As a convenience, --track without `-b` implies branch
 creation; see the description of --track below.
 
-When <paths> are given, this command does *not* switch
+When <paths> or --patch are given, this command does *not* switch
 branches.  It updates the named paths in the working tree from
 the index file, or from a named <tree-ish> (most often a commit).  In
 this case, the `-b` and `--track` options are meaningless and giving
@@ -45,9 +46,11 @@ file can be discarded to recreate the original conflicted merge result.
 OPTIONS
 -------
 -q::
+--quiet::
        Quiet, suppress feedback messages.
 
 -f::
+--force::
        When switching branches, proceed even if the index or the
        working tree differs from HEAD.  This is used to throw away
        local changes.
@@ -113,6 +116,16 @@ the conflicted merge in the specified paths.
        "merge" (default) and "diff3" (in addition to what is shown by
        "merge" style, shows the original contents).
 
+-p::
+--patch::
+       Interactively select hunks in the difference between the
+       <tree-ish> (or the index, if unspecified) and the working
+       tree.  The chosen hunks are then applied in reverse to the
+       working tree (and if a <tree-ish> was specified, the index).
++
+This means that you can use `git checkout -p` to selectively discard
+edits from your current working tree.
+
 <branch>::
        Branch to checkout; if it refers to a branch (i.e., a name that,
        when prepended with "refs/heads/", is a valid ref), then that
index be894af39ff6559affbf0617e4c5d8fe68c33fe8..9d291bdd266b49fe01b017dfce8e73dd288ad785 100644 (file)
@@ -27,8 +27,12 @@ OPTIONS
 -------
 -d::
        Remove untracked directories in addition to untracked files.
+       If an untracked directory is managed by a different git
+       repository, it is not removed by default.  Use -f option twice
+       if you really want to remove such a directory.
 
 -f::
+--force::
        If the git configuration specifies clean.requireForce as true,
        'git-clean' will refuse to run unless given -f or -n.
 
index 4072f40d7ae5417c51d1cddcf587b674aa1dc0e5..5ebcba1c7c6e749e2d80e7f98804be600fd548b3 100644 (file)
@@ -12,15 +12,16 @@ SYNOPSIS
 'git clone' [--template=<template_directory>]
          [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
          [-o <name>] [-u <upload-pack>] [--reference <repository>]
-         [--depth <depth>] [--] <repository> [<directory>]
+         [--depth <depth>] [--recursive] [--] <repository> [<directory>]
 
 DESCRIPTION
 -----------
 
 Clones a repository into a newly created directory, creates
 remote-tracking branches for each branch in the cloned repository
-(visible using `git branch -r`), and creates and checks out an initial
-branch equal to the cloned repository's currently active branch.
+(visible using `git branch -r`), and creates and checks out an
+initial branch that is forked from the cloned repository's
+currently active branch.
 
 After the clone, a plain `git fetch` without arguments will update
 all the remote-tracking branches, and a `git pull` without
@@ -72,11 +73,19 @@ These objects may be removed by normal git operations (such as 'git-commit')
 which automatically call `git gc --auto`. (See linkgit:git-gc[1].)
 If these objects are removed and were referenced by the cloned repository,
 then the cloned repository will become corrupt.
-
-
++
+Note that running `git repack` without the `-l` option in a repository
+cloned with `-s` will copy objects from the source repository into a pack
+in the cloned repository, removing the disk space savings of `clone -s`.
+It is safe, however, to run `git gc`, which uses the `-l` option by
+default.
++
+If you want to break the dependency of a repository cloned with `-s` on
+its source repository, you can simply run `git repack -a` to copy all
+objects from the source repository into a pack in the cloned repository.
 
 --reference <repository>::
-       If the reference repository is on the local machine
+       If the reference repository is on the local machine,
        automatically setup .git/objects/info/alternates to
        obtain objects from the reference repository.  Using
        an already existing repository as an alternate will
@@ -119,6 +128,13 @@ then the cloned repository will become corrupt.
        Instead of using the remote name 'origin' to keep track
        of the upstream repository, use <name>.
 
+--branch <name>::
+-b <name>::
+       Instead of pointing the newly created HEAD to the branch pointed
+       to by the cloned repository's HEAD, point to <name> branch
+       instead. In a non-bare repository, this is the branch that will
+       be checked out.
+
 --upload-pack <upload-pack>::
 -u <upload-pack>::
        When given, and the repository to clone from is accessed
@@ -139,6 +155,14 @@ then the cloned repository will become corrupt.
        with a long history, and would want to send in fixes
        as patches.
 
+--recursive::
+       After the clone is created, initialize all submodules within,
+       using their default settings. This is equivalent to running
+       'git submodule update --init --recursive' immediately after
+       the clone is finished. This option is ignored if the cloned
+       repository does not have a worktree/checkout (i.e. if any of
+       `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
+
 <repository>::
        The (possibly remote) repository to clone from.  See the
        <<URLS,URLS>> section below for more information on specifying
@@ -149,7 +173,7 @@ then the cloned repository will become corrupt.
        part of the source repository is used if no directory is
        explicitly given ("repo" for "/path/to/repo.git" and "foo"
        for "host.xz:foo/.git").  Cloning into an existing directory
-       is not allowed.
+       is only allowed if the directory is empty.
 
 :git-clone: 1
 include::urls.txt[]
index b5d81be7ecd60daa1a1d476441ca58e6b732d9ef..0578a40d841348b5ae484e1fe15ce637ecc4b830 100644 (file)
@@ -8,7 +8,7 @@ git-commit - Record changes to the repository
 SYNOPSIS
 --------
 [verse]
-'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend]
+'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
           [(-c | -C) <commit>] [-F <file> | -m <msg>]
           [--allow-empty] [--no-verify] [-e] [--author=<author>]
           [--cleanup=<mode>] [--] [[-i | -o ]<file>...]
@@ -42,10 +42,9 @@ The content to be added can be specified in several ways:
    by one which files should be part of the commit, before finalizing the
    operation.  Currently, this is done by invoking 'git-add --interactive'.
 
-The 'git-status' command can be used to obtain a
+The `--dry-run` option can be used to obtain a
 summary of what is included by any of the above for the next
-commit by giving the same set of parameters you would give to
-this command.
+commit by giving the same set of parameters (options and paths).
 
 If you make a commit and then find a mistake immediately after
 that, you can recover from it with 'git-reset'.
@@ -198,6 +197,11 @@ specified.
 --quiet::
        Suppress commit summary message.
 
+--dry-run::
+       Do not create a commit, but show a list of paths that are
+       to be committed, paths with local changes that will be left
+       uncommitted and paths that are untracked.
+
 \--::
        Do not interpret any more arguments as options.
 
index 7131ee3c66951de5981a82e304f4619de88064c5..f68b198205d3a4d808ce58e6b0ac03e70bb160a4 100644 (file)
@@ -69,7 +69,8 @@ OPTIONS
 
 --add::
        Adds a new line to the option without altering any existing
-       values.  This is the same as providing '^$' as the value_regex.
+       values.  This is the same as providing '^$' as the value_regex
+       in `--replace-all`.
 
 --get::
        Get the value for a given key (optionally filtered by a regex
@@ -155,7 +156,7 @@ See also <<FILES>>.
        When the color setting for `name` is undefined, the command uses
        `color.ui` as fallback.
 
---get-color name default::
+--get-color name [default]::
 
        Find the color configured for `name` (e.g. `color.diff.new`) and
        output it as the ANSI color escape sequence to the standard
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 c5261415643d359648900e17f522ba7b96fed44a..4ef03578ebc58f3b678e8805644ae8c0ad4b998b 100644 (file)
@@ -43,8 +43,7 @@ omit diff output for unmerged entries and just show "Unmerged".
 -q::
        Remain silent even on nonexistent files
 
-Output format
--------------
+
 include::diff-format.txt[]
 
 
index 26920d4f63cd213ff17ab28d8dd0dbea94482147..8b9ed2929980ec9928930496375df5e9d72e4b51 100644 (file)
@@ -34,8 +34,6 @@ include::diff-options.txt[]
        'git-diff-index' say that all non-checked-out files are up
        to date.
 
-Output format
--------------
 include::diff-format.txt[]
 
 Operating Modes
index 23b7abd3c6b0e02eb325983eaad66598c42fc8be..f2cef1260b7f74254b96ce65f8aa55267b618c29 100644 (file)
@@ -159,8 +159,7 @@ HEAD commits it finds, which is even more interesting.
 
 in case you care).
 
-Output format
--------------
+
 include::diff-format.txt[]
 
 
index a2f192fb7519117f8e47f3ec4608e3301c200dc3..0ac711230e24bf7238b2e3a215cdc3c4ce0d2087 100644 (file)
@@ -84,8 +84,7 @@ include::diff-options.txt[]
        the diff to the named paths (you can give directory
        names and get diff for all files under them).
 
-Output format
--------------
+
 include::diff-format.txt[]
 
 EXAMPLES
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 0c9eb567cb3e3af89a2e2613d1e074941fa30d13..75b06f33e73497fbb8916ef02d70ca59f4aab2f6 100644 (file)
@@ -36,6 +36,17 @@ when encountering a signed tag.  With 'strip', the tags will be made
 unsigned, with 'verbatim', they will be silently exported
 and with 'warn', they will be exported, but you will see a warning.
 
+--tag-of-filtered-object=(abort|drop|rewrite)::
+       Specify how to handle tags whose tagged objectis filtered out.
+       Since revisions and files to export can be limited by path,
+       tagged objects may be filtered completely.
++
+When asking to 'abort' (which is the default), this program will die
+when encountering such a tag.  With 'drop' it will omit such tags from
+the output.  With 'rewrite', if the tagged object is a commit, it will
+rewrite the tag to tag an ancestor commit (via parent rewriting; see
+linkgit:git-rev-list[1])
+
 -M::
 -C::
        Perform move and/or copy detection, as described in the
@@ -71,6 +82,20 @@ marks the same across runs.
        allow that.  So fake a tagger to be able to fast-import the
        output.
 
+--no-data::
+       Skip output of blob objects and instead refer to blobs via
+       their original SHA-1 hash.  This is useful when rewriting the
+       directory structure or history of a repository without
+       touching the contents of individual files.  Note that the
+       resulting stream can only be used by a repository which
+       already contains the necessary objects.
+
+[git-rev-list-args...]::
+       A list of arguments, acceptable to 'git-rev-parse' and
+       'git-rev-list', that specifies the specific objects and references
+       to export.  For example, `master\~10..master` causes the
+       current master reference to be exported along with all objects
+       added since its 10th ancestor commit.
 
 EXAMPLES
 --------
index ab527b5b316a81eddd8a0eefb501448a3097182f..2b40babb6ba1c5eb99ae2cc7748854f5e4dcd864 100644 (file)
@@ -12,6 +12,7 @@ SYNOPSIS
        [--index-filter <command>] [--parent-filter <command>]
        [--msg-filter <command>] [--commit-filter <command>]
        [--tag-name-filter <command>] [--subdirectory-filter <directory>]
+       [--prune-empty]
        [--original <namespace>] [-d <directory>] [-f | --force]
        [--] [<rev-list options>...]
 
@@ -305,6 +306,16 @@ range in addition to the new branch name.  The new branch name will
 point to the top-most revision that a 'git-rev-list' of this range
 will print.
 
+If you need to add 'Acked-by' lines to, say, the last 10 commits (none
+of which is a merge), use this command:
+
+--------------------------------------------------------
+git filter-branch --msg-filter '
+       cat &&
+       echo "Acked-by: Bugs Bunny <bunny@bugzilla.org>"
+' HEAD~10..HEAD
+--------------------------------------------------------
+
 *NOTE* the changes introduced by the commits, and which are not reverted
 by subsequent commits, will still be in the rewritten branch. If you want
 to throw out _changes_ together with the commits, you should use the
index 1c24796d66d5aeaeeccfd152c69cddba1953fd6c..a586950b48bac6eda630383714c0ccd1b28820a8 100644 (file)
@@ -18,8 +18,8 @@ Takes the list of merged objects on stdin and produces a suitable
 commit message to be used for the merge commit, usually to be
 passed as the '<merge-message>' argument of 'git-merge'.
 
-This script is intended mostly for internal use by scripts
-automatically invoking 'git-merge'.
+This command is intended mostly for internal use by scripts
+automatically invoking 'git merge'.
 
 OPTIONS
 -------
index 6f1fc80119600c419d2e3b98bac0c44f9cb09026..687e66759817bc2f349e8a5a89aa2c6731237b23 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git format-patch' [-k] [(-o|--output-directory) <dir> | --stdout]
-                  [--thread[=<style>]]
+                  [--no-thread | --thread[=<style>]]
                   [(--attach|--inline)[=<boundary>] | --no-attach]
                   [-s | --signoff]
                   [-n | --numbered | -N | --no-numbered]
@@ -124,17 +124,25 @@ include::diff-options.txt[]
        second part, with "Content-Disposition: inline".
 
 --thread[=<style>]::
-       Add In-Reply-To and References headers to make the second and
-       subsequent mails appear as replies to the first.  Also generates
-       the Message-Id header to reference.
+--no-thread::
+       Controls addition of In-Reply-To and References headers to
+       make the second and subsequent mails appear as replies to the
+       first.  Also controls generation of the Message-Id header to
+       reference.
 +
 The optional <style> argument can be either `shallow` or `deep`.
 'shallow' threading makes every mail a reply to the head of the
 series, where the head is chosen from the cover letter, the
 `\--in-reply-to`, and the first patch mail, in this order.  'deep'
-threading makes every mail a reply to the previous one.  If not
-specified, defaults to the 'format.thread' configuration, or `shallow`
-if that is not set.
+threading makes every mail a reply to the previous one.
++
+The default is --no-thread, unless the 'format.thread' configuration
+is set.  If --thread is specified without a style, it defaults to the
+style specified by 'format.thread' if any, or else `shallow`.
++
+Beware that the default for 'git send-email' is to thread emails
+itself.  If you want 'git format-patch' to take care of hreading, you
+will want to ensure that threading is disabled for 'git send-email'.
 
 --in-reply-to=Message-Id::
        Make the first mail (or all the mails with --no-thread) appear as a
index b292e9843aa9da86cd44bd07d3ce35053be32177..1f6df6ad6b374ee92952221856953d2c960a7f7e 100644 (file)
@@ -61,7 +61,7 @@ automatic consolidation of packs.
 
 --prune=<date>::
        Prune loose objects older than date (default is 2 weeks ago,
-       overrideable by the config variable `gc.pruneExpire`).  This
+       overridable by the config variable `gc.pruneExpire`).  This
        option is on by default.
 
 --no-prune::
@@ -106,7 +106,7 @@ much time is spent optimizing the delta compression of the objects in
 the repository when the --aggressive option is specified.  The larger
 the value, the more time is spent optimizing the delta compression.  See
 the documentation for the --window' option in linkgit:git-repack[1] for
-more details.  This defaults to 10.
+more details.  This defaults to 250.
 
 The optional configuration variable 'gc.pruneExpire' controls how old
 the unreferenced loose objects have to be before they are pruned.  The
index fccb82deb48ec75d25f948e7caa69a44e09f5832..8c700200f55e1a92545cfb8218c8c1ba2cedee68 100644 (file)
@@ -17,6 +17,7 @@ SYNOPSIS
           [-l | --files-with-matches] [-L | --files-without-match]
           [-z | --null]
           [-c | --count] [--all-match]
+          [--max-depth <depth>]
           [--color | --no-color]
           [-A <post-context>] [-B <pre-context>] [-C <context>]
           [-f <file>] [-e] <pattern>
@@ -47,6 +48,10 @@ OPTIONS
 -I::
        Don't match the pattern in binary files.
 
+--max-depth <depth>::
+       For each pathspec given on command line, descend at most <depth>
+       levels of directories. A negative value means no limit.
+
 -w::
 --word-regexp::
        Match the pattern only at word boundary (either begin at the
@@ -122,6 +127,14 @@ OPTIONS
 -<num>::
        A shortcut for specifying -C<num>.
 
+-p::
+--show-function::
+       Show the preceding line that contains the function name of
+       the match, unless the matching line is a function name itself.
+       The name is determined in the same way as 'git diff' works out
+       patch hunk headers (see 'Defining a custom hunk-header' in
+       linkgit:gitattributes[5]).
+
 -f <file>::
        Read patterns from <file>, one per line.
 
index 1fd0ff2610a1375bcf0defe2a234b2dee1a7997a..eba3cb4998b2732a22551aaf962ae1a742d221de 100644 (file)
@@ -8,7 +8,7 @@ git-init-db - Creates an empty git repository
 
 SYNOPSIS
 --------
-'git init-db' [-q | --quiet] [--template=<template_directory>] [--shared[=<permissions>]]
+'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]]
 
 
 DESCRIPTION
index 7151d12f349b7c6e265d5a4631029d71028a2c7d..f081b24d9d8233677388d437b8f7e6d9402b261b 100644 (file)
@@ -8,7 +8,7 @@ git-init - Create an empty git repository or reinitialize an existing one
 
 SYNOPSIS
 --------
-'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]]
+'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] [directory]
 
 
 OPTIONS
@@ -74,6 +74,9 @@ By default, the configuration flag receive.denyNonFastForwards is enabled
 in shared repositories, so that you cannot force a non fast-forwarding push
 into it.
 
+If you name a (possibly non-existent) directory at the end of the command
+line, the command is run inside the directory (possibly after creating it).
+
 --
 
 
index 22da21a54f625c434216945889127ec283d3d09f..0771f254436c9024b935ef745c3bcd384bc18465 100644 (file)
@@ -29,7 +29,7 @@ OPTIONS
        The HTTP daemon command-line that will be executed.
        Command-line options may be specified here, and the
        configuration file will be added at the end of the command-line.
-       Currently lighttpd, apache2 and webrick are supported.
+       Currently apache2, lighttpd, mongoose and webrick are supported.
        (Default: lighttpd)
 
 -m::
index 34cf4e5811d1a6f46fcbd333a2ff48c200eadff8..3d79de11ec36d0212610a4bec68789bdeec6c1fe 100644 (file)
@@ -37,8 +37,12 @@ include::diff-options.txt[]
        and <until>, see "SPECIFYING REVISIONS" section in
        linkgit:git-rev-parse[1].
 
---decorate::
-       Print out the ref names of any commits that are shown.
+--decorate[=short|full]::
+       Print out the ref names of any commits that are shown. If 'short' is
+       specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and
+       'refs/remotes/' will not be printed. If 'full' is specified, the
+       full ref name (including prefix) will be printed. The default option
+       is 'short'.
 
 --source::
        Print out the ref name given on the command line by which each
index 057a021eb50899ed5fe1ee5a7b6c59e7fc27becf..021066e95d88624d2ec6c90a9218a4fd70ec17b7 100644 (file)
@@ -44,7 +44,7 @@ OPTIONS
 
 -o::
 --others::
-       Show other files in the output
+       Show other (i.e. untracked) files in the output
 
 -i::
 --ignored::
index f68e5c5c1abd15350e679eebf976f0ac8516123b..c3fdccb4c2f66b8b3a3c6b60b21feac881b06cb4 100644 (file)
@@ -82,8 +82,10 @@ Output Format
 -------------
         <mode> SP <type> SP <object> TAB <file>
 
-When the `-z` option is not used, TAB, LF, and backslash characters
+Unless the `-z` option is used, TAB, LF, and backslash characters
 in pathnames are represented as `\t`, `\n`, and `\\`, respectively.
+This output format is compatible with what '--index-info --stdin' of
+'git update-index' expects.
 
 When the `-l` option is used, format changes to
 
index 8d95aaa30441c36a019e9d3d78ec451fbd40fdaf..996c3fcc6cde0b7ce1acf6bfb3815bac4dd6176c 100644 (file)
@@ -8,7 +8,7 @@ git-mailinfo - Extracts patch and authorship from a single e-mail message
 
 SYNOPSIS
 --------
-'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] <msg> <patch>
+'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
 
 
 DESCRIPTION
@@ -49,6 +49,25 @@ conversion, even with this flag.
 -n::
        Disable all charset re-coding of the metadata.
 
+--scissors::
+       Remove everything in body before a scissors line.  A line that
+       mainly consists of scissors (either ">8" or "8<") and perforation
+       (dash "-") marks is called a scissors line, and is used to request
+       the reader to cut the message at that line.  If such a line
+       appears in the body of the message before the patch, everything
+       before it (including the scissors line itself) is ignored when
+       this option is used.
++
+This is useful if you want to begin your message in a discussion thread
+with comments and suggestions on the message you are responding to, and to
+conclude it with a patch submission, separating the discussion and the
+beginning of the proposed commit log message with a scissors line.
++
+This can enabled by default with the configuration option mailinfo.scissors.
+
+--no-scissors::
+       Ignore scissors lines. Useful for overriding mailinfo.scissors settings.
+
 <msg>::
        The commit log message extracted from e-mail, usually
        except the title line which comes from e-mail Subject.
index 767486c770afd385d118ee5f9a6f9cd3ad0a2d73..ce5b369985c254ec5d986aa3dd250828cf3cc4cb 100644 (file)
@@ -8,12 +8,12 @@ git-merge-base - Find as good common ancestors as possible for a merge
 
 SYNOPSIS
 --------
-'git merge-base' [--all] <commit> <commit>...
+'git merge-base' [-a|--all] <commit> <commit>...
 
 DESCRIPTION
 -----------
 
-'git-merge-base' finds best common ancestor(s) between two commits to use
+'git merge-base' finds best common ancestor(s) between two commits to use
 in a three-way merge.  One common ancestor is 'better' than another common
 ancestor if the latter is an ancestor of the former.  A common ancestor
 that does not have any better common ancestor is a 'best common
@@ -27,8 +27,13 @@ commits on the command line.  As the most common special case, specifying only
 two commits on the command line means computing the merge base between
 the given two commits.
 
+As a consequence, the 'merge base' is not necessarily contained in each of the
+commit arguments if more than two commits are specified. This is different
+from linkgit:git-show-branch[1] when used with the `--merge-base` option.
+
 OPTIONS
 -------
+-a::
 --all::
        Output all merge bases for the commits, instead of just one.
 
index c04ae739edd409307d286fe31e6f87e41f977eac..d05f324462d23f64ba9563666e555a9eee3c443f 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
-       [-m <msg>] <remote> <remote>...
+       [-m <msg>] <remote>...
 'git merge' <msg> HEAD <remote>...
 
 DESCRIPTION
@@ -28,9 +28,10 @@ OPTIONS
 include::merge-options.txt[]
 
 -m <msg>::
-       The commit message to be used for the merge commit (in case
-       it is created). The 'git-fmt-merge-msg' script can be used
-       to give a good default for automated 'git-merge' invocations.
+       Set the commit message to be used for the merge commit (in
+       case one is created). The 'git fmt-merge-msg' command can be
+       used to give a good default for automated 'git merge'
+       invocations.
 
 <remote>...::
        Other branch heads to merge into our branch.  You need at
@@ -49,8 +50,8 @@ include::merge-config.txt[]
 
 branch.<name>.mergeoptions::
        Sets default options for merging into branch <name>. The syntax and
-       supported options are equal to that of 'git-merge', but option values
-       containing whitespace characters are currently not supported.
+       supported options are the same as those of 'git merge', but option
+       values containing whitespace characters are currently not supported.
 
 HOW MERGE WORKS
 ---------------
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 9c5660275b326661bf7dc9a5162e5177b8a62b0f..bdcb58526ec2e838949e079891a420802df477db 100644 (file)
@@ -28,6 +28,7 @@ committed.
 OPTIONS
 -------
 -f::
+--force::
        Force renaming or moving of a file even if the target exists
 -k::
         Skip move or rename actions which would lead to an error
index 7d4c1a75562de80aa80dfbfa665330b14ec948c6..2e4992970e84a1789cb97d35969976d12e5a30e1 100644 (file)
@@ -11,7 +11,8 @@ SYNOPSIS
 [verse]
 'git pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
        [--local] [--incremental] [--window=N] [--depth=N] [--all-progress]
-       [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
+       [--revs [--unpacked | --all]*] [--stdout | base-name]
+       [--keep-true-parents] < object-list
 
 
 DESCRIPTION
@@ -197,6 +198,10 @@ base-name::
        to force the version for the generated pack index, and to force
        64-bit index entries on objects located above the given offset.
 
+--keep-true-parents::
+       With this option, parents that are hidden by grafts are packed
+       nevertheless.
+
 
 Author
 ------
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 b5f26cee132622185457d92522fb932302dec97d..abfc6b6ead534311d8a29696c497ecf94ce4fd1a 100644 (file)
@@ -8,7 +8,7 @@ git-prune-packed - Remove extra objects that are already in pack files
 
 SYNOPSIS
 --------
-'git prune-packed' [-n] [-q]
+'git prune-packed' [-n|--dry-run] [-q|--quiet]
 
 
 DESCRIPTION
@@ -28,10 +28,12 @@ disk storage, etc.
 OPTIONS
 -------
 -n::
+--dry-run::
         Don't actually remove any objects, only show those that would have been
         removed.
 
 -q::
+--quiet::
        Squelch the progress indicator.
 
 Author
index fd53c49fb886b196ba79e40c4c9c4efe0dae5432..37c88953d1a08c0498c794aeb4d0d38eac8a36db 100644 (file)
@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects
 SYNOPSIS
 --------
 [verse]
-'git push' [--all | --mirror | --tags] [--dry-run] [--receive-pack=<git-receive-pack>]
+'git push' [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
           [--repo=<repository>] [-f | --force] [-v | --verbose]
           [<repository> <refspec>...]
 
@@ -82,9 +82,15 @@ nor in any Push line of the corresponding remotes file---see below).
        if the configuration option `remote.<remote>.mirror` is
        set.
 
+-n::
 --dry-run::
        Do everything except actually send the updates.
 
+--porcelain::
+       Produce machine-readable output.  The output status line for each ref
+       will be tab-separated and sent to stdout instead of stderr.  The full
+       symbolic names of the refs will be given.
+
 --tags::
        All refs under `$GIT_DIR/refs/tags` are pushed, in
        addition to refspecs explicitly listed on the command
@@ -132,6 +138,11 @@ useful if you write an alias or script around 'git-push'.
 --verbose::
        Run verbosely.
 
+-q::
+--quiet::
+       Suppress all output, including the listing of updated refs,
+       unless an error occurs.
+
 include::urls-remotes.txt[]
 
 OUTPUT
@@ -148,6 +159,12 @@ representing the status of a single ref. Each line is of the form:
  <flag> <summary> <from> -> <to> (<reason>)
 -------------------------------
 
+If --porcelain is used, then each line of the output is of the form:
+
+-------------------------------
+ <flag> \t <from>:<to> \t <summary> (<reason>)
+-------------------------------
+
 flag::
        A single character indicating the status of the ref. This is
        blank for a successfully pushed ref, `!` for a ref that was
@@ -184,6 +201,92 @@ reason::
        refs, no explanation is needed. For a failed ref, the reason for
        failure is described.
 
+Note about fast-forwards
+------------------------
+
+When an update changes a branch (or more in general, a ref) that used to
+point at commit A to point at another commit B, it is called a
+fast-forward update if and only if B is a descendant of A.
+
+In a fast-forward update from A to B, the set of commits that the original
+commit A built on top of is a subset of the commits the new commit B
+builds on top of.  Hence, it does not lose any history.
+
+In contrast, a non-fast-forward update will lose history.  For example,
+suppose you and somebody else started at the same commit X, and you built
+a history leading to commit B while the other person built a history
+leading to commit A.  The history looks like this:
+
+----------------
+
+      B
+     /
+ ---X---A
+
+----------------
+
+Further suppose that the other person already pushed changes leading to A
+back to the original repository you two obtained the original commit X.
+
+The push done by the other person updated the branch that used to point at
+commit X to point at commit A.  It is a fast-forward.
+
+But if you try to push, you will attempt to update the branch (that
+now points at A) with commit B.  This does _not_ fast-forward.  If you did
+so, the changes introduced by commit A will be lost, because everybody
+will now start building on top of B.
+
+The command by default does not allow an update that is not a fast-forward
+to prevent such loss of history.
+
+If you do not want to lose your work (history from X to B) nor the work by
+the other person (history from X to A), you would need to first fetch the
+history from the repository, create a history that contains changes done
+by both parties, and push the result back.
+
+You can perform "git pull", resolve potential conflicts, and "git push"
+the result.  A "git pull" will create a merge commit C between commits A
+and B.
+
+----------------
+
+      B---C
+     /   /
+ ---X---A
+
+----------------
+
+Updating A with the resulting merge commit will fast-forward and your
+push will be accepted.
+
+Alternatively, you can rebase your change between X and B on top of A,
+with "git pull --rebase", and push the result back.  The rebase will
+create a new commit D that builds the change between X and B on top of
+A.
+
+----------------
+
+      B   D
+     /   /
+ ---X---A
+
+----------------
+
+Again, updating A with this commit will fast-forward and your push will be
+accepted.
+
+There is another common situation where you may encounter non-fast-forward
+rejection when you try to push, and it is possible even when you are
+pushing into a repository nobody else pushes into. After you push commit
+A yourself (in the first picture in this section), replace it with "git
+commit --amend" to produce commit B, and you try to push it out, because
+forgot that you have pushed A out already. In such a case, and only if
+you are certain that nobody in the meantime fetched your earlier commit A
+(and started building on top of it), you can run "git push --force" to
+overwrite it. In other words, "git push --force" is a method reserved for
+a case where you do mean to lose history.
+
+
 Examples
 --------
 
index d4037de5124010e9c90dcc97e8b64e6011dbed21..579e8d2f3ba54e393a60a8b4d8a4ad6a95cb79f1 100644 (file)
@@ -9,7 +9,7 @@ git-quiltimport - Applies a quilt patchset onto the current branch
 SYNOPSIS
 --------
 [verse]
-'git quiltimport' [--dry-run] [--author <author>] [--patches <dir>]
+'git quiltimport' [--dry-run | -n] [--author <author>] [--patches <dir>]
 
 
 DESCRIPTION
index 7160fa1536e3af2edbdfb6d1049f2145bf2b4f50..4a932b08c6ed4f1de46d51fd92046d5190f831a7 100644 (file)
@@ -8,7 +8,10 @@ git-read-tree - Reads tree information into the index
 
 SYNOPSIS
 --------
-'git read-tree' (<tree-ish> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
+'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
+               [-u [--exclude-per-directory=<gitignore>] | -i]]
+               [--index-output=<file>]
+               <tree-ish1> [<tree-ish2> [<tree-ish3>]]
 
 
 DESCRIPTION
index 3d5a066c31675e502eb027dde824d1966c9c0f09..0aefc34d0d311030687563c56eaf3ac85a687e28 100644 (file)
@@ -231,12 +231,15 @@ 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.
 
+-q::
+--quiet::
+       Be quiet. Implies --no-stat.
+
 -v::
 --verbose::
        Be verbose. Implies --stat.
@@ -265,8 +268,9 @@ OPTIONS
        exit with the message "Current branch is up to date" in such a
        situation.
 
+--ignore-whitespace::
 --whitespace=<option>::
-       This flag is passed to the 'git-apply' program
+       These flag are passed to the 'git-apply' program
        (see linkgit:git-apply[1]) that applies the patch.
        Incompatible with the --interactive option.
 
diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
new file mode 100644 (file)
index 0000000..173ee23
--- /dev/null
@@ -0,0 +1,71 @@
+git-remote-helpers(1)
+=====================
+
+NAME
+----
+git-remote-helpers - Helper programs for interoperation with remote git
+
+SYNOPSIS
+--------
+'git remote-<transport>' <remote>
+
+DESCRIPTION
+-----------
+
+These programs are normally not used directly by end users, but are
+invoked by various git programs that interact with remote repositories
+when the repository they would operate on will be accessed using
+transport code not linked into the main git binary. Various particular
+helper programs will behave as documented here.
+
+COMMANDS
+--------
+
+Commands are given by the caller on the helper's standard input, one per line.
+
+'capabilities'::
+       Lists the capabilities of the helper, one per line, ending
+       with a blank line.
+
+'list'::
+       Lists the refs, one per line, in the format "<value> <name>
+       [<attr> ...]". The value may be a hex sha1 hash, "@<dest>" for
+       a symref, or "?" to indicate that the helper could not get the
+       value of the ref. A space-separated list of attributes follows
+       the name; unrecognized attributes are ignored. After the
+       complete list, outputs a blank line.
+
+'fetch' <sha1> <name>::
+       Fetches the given object, writing the necessary objects to the
+       database. Outputs a blank line when the fetch is
+       complete. Only objects which were reported in the ref list
+       with a sha1 may be fetched this way.
++
+Supported if the helper has the "fetch" capability.
+
+If a fatal error occurs, the program writes the error message to
+stderr and exits. The caller should expect that a suitable error
+message has been printed if the child closes the connection without
+completing a valid response for the current command.
+
+Additional commands may be supported, as may be determined from
+capabilities reported by the helper.
+
+CAPABILITIES
+------------
+
+'fetch'::
+       This helper supports the 'fetch' command.
+
+REF LIST ATTRIBUTES
+-------------------
+
+None are defined yet, but the caller must accept any which are supplied.
+
+Documentation
+-------------
+Documentation by Daniel Barkalow.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index 9e2b4eaa385db66ffe0c547f0452d29e9e3dc484..82a3d296736e515d4f99732e31369c8cdccc2cfd 100644 (file)
@@ -114,14 +114,14 @@ These stale branches have already been removed from the remote repository
 referenced by <name>, but are still locally available in
 "remotes/<name>".
 +
-With `--dry-run` option, report what branches will be pruned, but do no
+With `--dry-run` option, report what branches will be pruned, but do not
 actually prune them.
 
 'update'::
 
 Fetch updates for a named set of remotes in the repository as defined by
 remotes.<group>.  If a named group is not specified on the command line,
-the configuration parameter remotes.default will get used; if
+the configuration parameter remotes.default will be used; if
 remotes.default is not defined, all remotes which do not have the
 configuration parameter remote.<name>.skipDefaultUpdate set to true will
 be updated.  (See linkgit:git-config[1]).
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
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
new file mode 100644 (file)
index 0000000..915cb77
--- /dev/null
@@ -0,0 +1,71 @@
+git-replace(1)
+==============
+
+NAME
+----
+git-replace - Create, list, delete refs to replace objects
+
+SYNOPSIS
+--------
+[verse]
+'git replace' [-f] <object> <replacement>
+'git replace' -d <object>...
+'git replace' -l [<pattern>]
+
+DESCRIPTION
+-----------
+Adds a 'replace' reference in `.git/refs/replace/`
+
+The name of the 'replace' reference is the SHA1 of the object that is
+replaced. The content of the replace reference is the SHA1 of the
+replacement object.
+
+Unless `-f` is given, the replace reference must not yet exist in
+`.git/refs/replace/` directory.
+
+OPTIONS
+-------
+-f::
+       If an existing replace ref for the same object exists, it will
+       be overwritten (instead of failing).
+
+-d::
+       Delete existing replace refs for the given objects.
+
+-l <pattern>::
+       List replace refs for objects that match the given pattern (or
+       all if no pattern is given).
+       Typing "git replace" without arguments, also lists all replace
+       refs.
+
+BUGS
+----
+Comparing blobs or trees that have been replaced with those that
+replace them will not work properly. And using 'git reset --hard' to
+go back to a replaced commit will move the branch to the replacement
+commit instead of the replaced commit.
+
+There may be other problems when using 'git rev-list' related to
+pending objects. And of course things may break if an object of one
+type is replaced by an object of another type (for example a blob
+replaced by a commit).
+
+SEE ALSO
+--------
+linkgit:git-tag[1]
+linkgit:git-branch[1]
+
+Author
+------
+Written by Christian Couder <chriscool@tuxfamily.org> and Junio C
+Hamano <gitster@pobox.com>, based on 'git tag' by Kristian Hogsberg
+<krh@redhat.com> and Carlos Rica <jasampler@gmail.com>.
+
+Documentation
+--------------
+Documentation by Christian Couder <chriscool@tuxfamily.org> and the
+git-list <git@vger.kernel.org>, based on 'git tag' documentation.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index 64715c17da6c313a1cec4c353300eb0faee2313b..7dd515b8ccde701ccf5743f80b3b762813ea90c9 100644 (file)
@@ -12,18 +12,18 @@ 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
+You need to set the configuration variable rerere.enabled in order to
 enable this command.
 
 
@@ -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 abb25d1c00c97144b1f3709e408fe9cad613e623..469cf6dbacb8de24b5dd0dd78d63dd9ecc8fbd01 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 [verse]
 'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
 'git reset' [-q] [<commit>] [--] <paths>...
+'git reset' --patch [<commit>] [--] [<paths>...]
 
 DESCRIPTION
 -----------
@@ -23,8 +24,9 @@ the undo in the history.
 If you want to undo a commit other than the latest on a branch,
 linkgit:git-revert[1] is your friend.
 
-The second form with 'paths' is used to revert selected paths in
-the index from a given commit, without moving HEAD.
+The second and third forms with 'paths' and/or --patch are used to
+revert selected paths in the index from a given commit, without moving
+HEAD.
 
 
 OPTIONS
@@ -50,6 +52,15 @@ OPTIONS
        and updates the files that are different between the named commit
        and the current commit in the working tree.
 
+-p::
+--patch::
+       Interactively select hunks in the difference between the index
+       and <commit> (defaults to HEAD).  The chosen hunks are applied
+       in reverse to the index.
++
+This means that `git reset -p` is the opposite of `git add -p` (see
+linkgit:git-add[1]).
+
 -q::
        Be quiet, only report errors.
 
index 1c9cc28895a6ea3fcfd978f940e3fa327219de0a..3341d1b62f34fa99afcdab91da1191ceacc2c24f 100644 (file)
@@ -14,6 +14,7 @@ SYNOPSIS
             [ \--max-age=timestamp ]
             [ \--min-age=timestamp ]
             [ \--sparse ]
+            [ \--merges ]
             [ \--no-merges ]
             [ \--first-parent ]
             [ \--remove-empty ]
@@ -50,20 +51,26 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Lists commit objects in reverse chronological order starting at the
-given commit(s), taking ancestry relationship into account.  This is
-useful to produce human-readable log output.
+List commits that are reachable by following the `parent` links from the
+given commit(s), but exclude commits that are reachable from the one(s)
+given with a '{caret}' in front of them.  The output is given in reverse
+chronological order by default.
 
-Commits which are stated with a preceding '{caret}' cause listing to
-stop at that point. Their parents are implied. Thus the following
-command:
+You can think of this as a set operation.  Commits given on the command
+line form a set of commits that are reachable from any of them, and then
+commits reachable from any of the ones given with '{caret}' in front are
+subtracted from that set.  The remaining commits are what comes out in the
+command's output.  Various other options and paths parameters can be used
+to further limit the result.
+
+Thus, the following command:
 
 -----------------------------------------------------------------------
        $ git rev-list foo bar ^baz
 -----------------------------------------------------------------------
 
-means "list all the commits which are included in 'foo' and 'bar', but
-not in 'baz'".
+means "list all the commits which are reachable from 'foo' or 'bar', but
+not from 'baz'".
 
 A special notation "'<commit1>'..'<commit2>'" can be used as a
 short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of
@@ -83,7 +90,7 @@ between the two operands.  The following two commands are equivalent:
        $ git rev-list A...B
 -----------------------------------------------------------------------
 
-'git-rev-list' is a very essential git program, since it
+'rev-list' is a very essential git command, since it
 provides the ability to build and traverse commit ancestry graphs. For
 this reason, it has a lot of different options that enables it to be
 used by commands as different as 'git-bisect' and
index 52c353e674761bf4897484a261c702e5cc02f18a..82045a2522799ccf3a03479bf4f4cd1fa1809879 100644 (file)
@@ -30,6 +30,16 @@ OPTIONS
        Only meaningful in `--parseopt` mode. Tells the option parser to echo
        out the first `--` met instead of skipping it.
 
+--stop-at-non-option::
+       Only meaningful in `--parseopt` mode.  Lets the option parser stop at
+       the first non-option argument.  This can be used to parse sub-commands
+       that take options themself.
+
+--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 +74,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 +417,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..767cf4d4bdcdfbb6e76b3680b0e89af2186d632a 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.
@@ -134,8 +142,11 @@ 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'.
+       servers typically listen to smtp port 25, but may also listen to
+       submission port 587, or the common SSL smtp port 465);
+       symbolic port names (e.g. "submission" instead of 587)
+       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 +166,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 +194,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.
 --
 +
@@ -202,10 +213,22 @@ specified, as well as 'body' if --no-signed-off-cc is specified.
        value; if that is unspecified, default to --no-suppress-from.
 
 --[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
-       value; if that is unspecified, default to --thread.
+       If this is set, the In-Reply-To and References headers will be
+       added to each email sent.  Whether each mail refers to the
+       previous email (`deep` threading per 'git format-patch'
+       wording) or to the first email (`shallow` threading) is
+       governed by "--[no-]chain-reply-to".
++
+If disabled with "--no-thread", those headers will not be added
+(unless specified with --in-reply-to).  Default is the value of the
+'sendemail.thread' configuration value; if that is unspecified,
+default to --thread.
++
+It is up to the user to ensure that no In-Reply-To header already
+exists when 'git send-email' is asked to add it (especially note that
+'git format-patch' can be configured to do the threading itself).
+Failure to do so may not produce the expected result in the
+recipient's MUA.
 
 
 Administering
@@ -230,6 +253,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 +275,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..734336119c6b1f7ea8241f0404eaa3ba2ae10f69 100644 (file)
@@ -8,9 +8,12 @@ git-show-branch - Show branches and their commits
 SYNOPSIS
 --------
 [verse]
-'git show-branch' [--all] [--remotes] [--topo-order] [--current]
+'git show-branch' [-a|--all] [-r|--remotes] [--topo-order | --date-order]
+               [--current] [--color | --no-color] [--sparse]
                [--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 +60,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
@@ -74,9 +82,11 @@ OPTIONS
        Synonym to `--more=-1`
 
 --merge-base::
-       Instead of showing the commit list, just act like the
-       'git-merge-base -a' command, except that it can accept
-       more than two heads.
+       Instead of showing the commit list, determine possible
+       merge bases for the specified commits. All merge bases
+       will be contained in all specified commits. This is
+       different from how linkgit:git-merge-base[1] handles
+       the case of three or more commits.
 
 --independent::
        Among the <reference>s given, display only the ones that
@@ -107,6 +117,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 2f173fff356282df7c906da6edeac1fcd4430025..f4429bdc6863ce1d115b47282f2c04a4d9093d29 100644 (file)
@@ -9,8 +9,9 @@ SYNOPSIS
 --------
 [verse]
 'git show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
-            [-s|--hash] [--abbrev] [--tags] [--heads] [--] <pattern>...
-'git show-ref' --exclude-existing[=pattern]
+            [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags]
+            [--heads] [--] <pattern>...
+'git show-ref' --exclude-existing[=<pattern>] < ref-list
 
 DESCRIPTION
 -----------
@@ -24,7 +25,7 @@ The --exclude-existing form is a filter that does the inverse, it shows the
 refs from stdin that don't exist in the local repository.
 
 Use of this utility is encouraged in favor of directly accessing files under
-in the `.git` directory.
+the `.git` directory.
 
 OPTIONS
 -------
@@ -48,9 +49,9 @@ OPTIONS
        appended.
 
 -s::
---hash::
+--hash[=<n>]::
 
-       Only show the SHA1 hash, not the reference name. When also using
+       Only show the SHA1 hash, not the reference name. When combined with
        --dereference the dereferenced tag will still be shown after the SHA1.
 
 --verify::
@@ -59,11 +60,10 @@ OPTIONS
        Aside from returning an error code of 1, it will also print an error
        message if '--quiet' was not specified.
 
---abbrev::
---abbrev=len::
+--abbrev[=<n>]::
 
        Abbreviate the object name.  When using `--hash`, you do
-       not have to say `--hash --abbrev`; `--hash=len` would do.
+       not have to say `--hash --abbrev`; `--hash=n` would do.
 
 -q::
 --quiet::
@@ -71,8 +71,7 @@ OPTIONS
        Do not print any results to stdout. When combined with '--verify' this
        can be used to silently check if a reference exists.
 
---exclude-existing::
---exclude-existing=pattern::
+--exclude-existing[=<pattern>]::
 
        Make 'git-show-ref' act as a filter that reads refs from stdin of the
        form "^(?:<anything>\s)?<refname>(?:\^\{\})?$" and performs the
index 051f94d26f9f057cbd4db0d6886ca1a4d33c912c..fafe728f890d5020ffc5f1ab28deb4b3d02313a5 100644 (file)
@@ -9,10 +9,11 @@ SYNOPSIS
 --------
 [verse]
 'git stash' list [<options>]
-'git stash' (show | drop | pop ) [<stash>]
-'git stash' apply [--index] [<stash>]
+'git stash' show [<stash>]
+'git stash' drop [-q|--quiet] [<stash>]
+'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [save [--keep-index] [<message>]]
+'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]
 'git stash' clear
 'git stash' create
 
@@ -41,15 +42,27 @@ is also possible).
 OPTIONS
 -------
 
-save [--keep-index] [<message>]::
+save [--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::
 
        Save your local modifications to a new 'stash', and run `git reset
-       --hard` to revert them.  This is the default action when no
-       subcommand is given. The <message> part is optional and gives
-       the description along with the stashed state.
+       --hard` to revert them.  The <message> part is optional and gives
+       the description along with the stashed state.  For quickly making
+       a snapshot, you can omit _both_ "save" and <message>, but giving
+       only <message> does not trigger this action to prevent a misspelled
+       subcommand from making an unwanted stash.
 +
 If the `--keep-index` option is used, all changes already added to the
 index are left intact.
++
+With `--patch`, you can interactively select hunks from in the diff
+between HEAD and the working tree to be stashed.  The stash entry is
+constructed such that its index state is the same as the index state
+of your repository, and its worktree contains only the changes you
+selected interactively.  The selected changes are then rolled back
+from your worktree.
++
+The `--patch` option implies `--keep-index`.  You can use
+`--no-keep-index` to override this.
 
 list [<options>]::
 
@@ -65,7 +78,8 @@ stash@{1}: On master: 9cc0589... Add git-stash
 ----------------------------------------------------------------
 +
 The command takes options applicable to the 'git-log'
-command to control what is shown and how. See linkgit:git-log[1].
+command to control what is shown and how. If no options are set, the
+default is `-n 10`. See linkgit:git-log[1].
 
 show [<stash>]::
 
@@ -75,19 +89,27 @@ show [<stash>]::
        it will accept any format known to 'git-diff' (e.g., `git stash show
        -p stash@\{1}` to view the second most recent stash in patch form).
 
-apply [--index] [<stash>]::
+pop [--index] [-q|--quiet] [<stash>]::
 
-       Restore the changes recorded in the stash on top of the current
-       working tree state.  When no `<stash>` is given, applies the latest
-       one.  The working directory must match the index.
+       Remove a single stashed state from the stash list and apply it
+       on top of the current working tree state, i.e., do the inverse
+       operation of `git stash save`. The working directory must
+       match the index.
 +
-This operation can fail with conflicts; you need to resolve them
-by hand in the working tree.
+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.
 +
 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] [-q|--quiet] [<stash>]::
+
+       Like `pop`, but do not remove the state from the stash list.
 
 branch <branchname> [<stash>]::
 
@@ -105,19 +127,14 @@ no conflicts.
 
 clear::
        Remove all the stashed states. Note that those states will then
-       be subject to pruning, and may be difficult or impossible to recover.
+       be subject to pruning, and may be impossible to recover (see
+       'Examples' below for a possible strategy).
 
-drop [<stash>]::
+drop [-q|--quiet] [<stash>]::
 
        Remove a single stashed state from the stash list. When no `<stash>`
        is given, it removes the latest one. i.e. `stash@\{0}`
 
-pop [<stash>]::
-
-       Remove a single stashed state from the stash list and apply on top
-       of the current working tree state. When no `<stash>` is given,
-       `stash@\{0}` is assumed. See also `apply`.
-
 create::
 
        Create a stash (which is a regular commit object) and return its
@@ -163,7 +180,7 @@ $ git pull
 file foobar not up to date, cannot merge.
 $ git stash
 $ git pull
-$ git stash apply
+$ git stash pop
 ----------------------------------------------------------------
 
 Interrupted workflow::
@@ -192,7 +209,7 @@ You can use 'git-stash' to simplify the above, like this:
 $ git stash
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git stash apply
+$ git stash pop
 # ... continue hacking ...
 ----------------------------------------------------------------
 
@@ -214,6 +231,20 @@ $ edit/build/test remaining parts
 $ git commit foo -m 'Remaining parts'
 ----------------------------------------------------------------
 
+Recovering stashes that were cleared/dropped erroneously::
+
+If you mistakenly drop or clear stashes, they cannot be recovered
+through the normal safety mechanisms.  However, you can try the
+following incantation to get a list of stashes that are still in your
+repository, but not reachable any more:
++
+----------------------------------------------------------------
+git fsck --unreachable |
+grep commit | cut -d\  -f3 |
+xargs git log --merges --no-walk --grep=WIP
+----------------------------------------------------------------
+
+
 SEE ALSO
 --------
 linkgit:git-checkout[1],
index 3b8df4467377d73d613f76875c725cbf8544ee77..5ccdd18c89381e81fc616facb22bd7fee6faf964 100644 (file)
@@ -9,12 +9,14 @@ git-submodule - Initialize, update or inspect submodules
 SYNOPSIS
 --------
 [verse]
-'git submodule' [--quiet] add [-b branch] [--] <repository> <path>
-'git submodule' [--quiet] status [--cached] [--] [<path>...]
+'git submodule' [--quiet] add [-b branch]
+             [--reference <repository>] [--] <repository> <path>
+'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
-'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--] [<path>...]
-'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
-'git submodule' [--quiet] foreach <command>
+'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
+             [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] foreach [--recursive] <command>
 'git submodule' [--quiet] sync [--] [<path>...]
 
 
@@ -98,6 +100,9 @@ status::
        initialized and `+` if the currently checked out submodule commit
        does not match the SHA-1 found in the index of the containing
        repository. This command is the default command for 'git-submodule'.
++
+If '--recursive' is specified, this command will recurse into nested
+submodules, and show their status as well.
 
 init::
        Initialize the submodules, i.e. register each submodule name
@@ -113,32 +118,45 @@ 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' or
+       '--merge' is specified or the key `submodule.$name.update` is set to
+       `rebase` or `merge`.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
 submodule with the --init option.
++
+If '--recursive' is specified, this command will recurse into the
+registered submodules, and update any nested submodules within.
 
 summary::
        Show commit summary between the given commit (defaults to HEAD) and
        working tree/index. For a submodule in question, a series of commits
        in the submodule between the given super project commit and the
-       index or working tree (switched by --cached) are shown.
+       index or working tree (switched by --cached) are shown. If the option
+       --files is given, show the series of commits in the submodule between
+       the index of the super project and the working tree of the submodule
+       (this option doesn't allow to use the --cached option or to provide an
+       explicit commit).
 
 foreach::
        Evaluates an arbitrary shell command in each checked out submodule.
-       The command has access to the variables $path and $sha1:
+       The command has access to the variables $name, $path and $sha1:
+       $name is the name of the relevant submodule section in .gitmodules,
        $path is the name of the submodule directory relative to the
        superproject, and $sha1 is the commit as recorded in the superproject.
        Any submodules defined in the superproject but not checked out are
        ignored by this command. Unless given --quiet, foreach prints the name
        of each submodule before evaluating the command.
+       If --recursive is given, submodules are traversed recursively (i.e.
+       the given shell command is evaluated in nested submodules as well).
        A non-zero return from the command in any submodule causes
        the processing to terminate. This can be overridden by adding '|| :'
        to the end of the command.
 +
-As an example, "git submodule foreach 'echo $path `git rev-parse HEAD`' will
-show the path and currently checked out commit for each submodule.
+As an example, +git submodule foreach \'echo $path {backtick}git
+rev-parse HEAD{backtick}'+ will show the path and currently checked out
+commit for each submodule.
 
 sync::
        Synchronizes submodules' remote URL configuration setting
@@ -164,6 +182,11 @@ OPTIONS
        commands typically use the commit found in the submodule HEAD, but
        with this option, the commit stored in the index is used instead.
 
+--files::
+       This option is only valid for the summary command. This command
+       compares the commit in the index with that in the submodule HEAD
+       when this option is used.
+
 -n::
 --summary-limit::
        This option is only valid for the summary command.
@@ -177,6 +200,39 @@ OPTIONS
        This option is only valid for the update command.
        Don't fetch new objects from the remote site.
 
+--merge::
+       This option is only valid for the update command.
+       Merge the commit recorded in the superproject into the current branch
+       of the submodule. If this option is given, the submodule's HEAD will
+       not be detached. If a merge failure prevents this process, you will
+       have to resolve the resulting conflicts within the submodule with the
+       usual conflict resolution tools.
+       If the key `submodule.$name.update` is set to `merge`, this option is
+       implicit.
+
+--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.
+
+--recursive::
+       This option is only valid for foreach, update and status commands.
+       Traverse submodules recursively. The operation is performed not
+       only in the submodules of the current repo, but also
+       in any nested submodules inside those submodules (and so on).
+
 <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..1812890a7e986b2632972390ea4646331c0c922b 100644 (file)
@@ -3,7 +3,7 @@ git-svn(1)
 
 NAME
 ----
-git-svn - Bidirectional operation between a single Subversion branch and git
+git-svn - Bidirectional operation between a Subversion repository and git
 
 SYNOPSIS
 --------
@@ -11,27 +11,25 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-'git-svn' is a simple conduit for changesets between Subversion and git.
+'git svn' is a simple conduit for changesets between Subversion and git.
 It provides a bidirectional flow of changes between a Subversion and a git
 repository.
 
-'git-svn' can track a single Subversion branch simply by using a
-URL to the branch, follow branches laid out in the Subversion recommended
-method (trunk, branches, tags directories) with the --stdlayout option, or
-follow branches in any layout with the -T/-t/-b options (see options to
-'init' below, and also the 'clone' command).
+'git svn' can track a standard Subversion repository,
+following the common "trunk/branches/tags" layout, with the --stdlayout option.
+It can also follow branches and tags in any layout with the -T/-t/-b options
+(see options to 'init' below, and also the 'clone' command).
 
-Once tracking a Subversion branch (with any of the above methods), the git
+Once tracking a Subversion repository (with any of the above methods), the git
 repository can be updated from Subversion by the 'fetch' command and
 Subversion updated from git by the 'dcommit' command.
 
 COMMANDS
 --------
---
 
 'init'::
        Initializes an empty git repository with additional
-       metadata directories for 'git-svn'.  The Subversion URL
+       metadata directories for 'git svn'.  The Subversion URL
        may be specified as a command-line argument, or as full
        URL arguments to -T/-t/-b.  Optionally, the target
        directory to operate on can be specified as a second
@@ -48,8 +46,11 @@ COMMANDS
 --stdlayout;;
        These are optional command-line options for init.  Each of
        these flags can point to a relative repository path
-       (--tags=project/tags') or a full url
-       (--tags=https://foo.org/project/tags). The option --stdlayout is
+       (--tags=project/tags) or a full url
+       (--tags=https://foo.org/project/tags).
+       You can specify more than one --tags and/or --branches options, in case
+       your Subversion repository places tags or branches under multiple paths.
+       The option --stdlayout is
        a shorthand way of setting trunk,tags,branches as the relative paths,
        which is the Subversion default. If any of the other options are given
        as well, they take precedence.
@@ -61,16 +62,6 @@ COMMANDS
        Set the 'useSvnsyncProps' option in the [svn-remote] config.
 --rewrite-root=<URL>;;
        Set the 'rewriteRoot' option in the [svn-remote] config.
---use-log-author;;
-       When retrieving svn commits into git (as part of fetch, rebase, or
-       dcommit operations), look for the first From: or Signed-off-by: line
-       in the log message and use that as the author string.
---add-author-from;;
-       When committing to svn from git (as part of commit or dcommit
-       operations), if the existing log message doesn't already have a
-       From: or Signed-off-by: line, append a From: line based on the
-       git commit's author string.  If you use this, then --use-log-author
-       will retrieve a valid author string for all commits.
 --username=<USER>;;
        For transports that SVN handles authentication for (http,
        https, and plain svn), specify the username.  For other
@@ -89,6 +80,17 @@ COMMANDS
        When passed to 'init' or 'clone' this regular expression will
        be preserved as a config key.  See 'fetch' for a description
        of '--ignore-paths'.
+--no-minimize-url;;
+       When tracking multiple directories (using --stdlayout,
+       --branches, or --tags options), git svn will attempt to connect
+       to the root (or highest allowed level) of the Subversion
+       repository.  This default allows better tracking of history if
+       entire projects are moved within a repository, but may cause
+       issues on repositories where read access restrictions are in
+       place.  Passing '--no-minimize-url' will allow git svn to
+       accept URLs as-is without attempting to connect to a higher
+       level directory.  This option is off by default when only
+       one URL/branch is tracked (it would do little good).
 
 'fetch'::
        Fetch unfetched revisions from the Subversion remote we are
@@ -98,38 +100,57 @@ COMMANDS
 
 --localtime;;
        Store Git commit times in the local timezone instead of UTC.  This
-       makes 'git-log' (even without --date=local) show the same times
+       makes 'git log' (even without --date=local) show the same times
        that `svn log` would in the local timezone.
-
---parent;;
-       Fetch only from the SVN parent of the current HEAD.
-
++
 This doesn't interfere with interoperating with the Subversion
 repository you cloned from, but if you wish for your local Git
 repository to be able to interoperate with someone else's local Git
 repository, either don't use this option or you should both use it in
 the same local timezone.
 
+--parent;;
+       Fetch only from the SVN parent of the current HEAD.
+
 --ignore-paths=<regex>;;
        This allows one to specify a Perl regular expression that will
        cause skipping of all matching paths from checkout from SVN.
        The '--ignore-paths' option should match for every 'fetch'
        (including automatic fetches due to 'clone', 'dcommit',
        'rebase', etc) on a given repository.
-
++
+[verse]
 config key: svn-remote.<name>.ignore-paths
-
-       If the ignore-paths config key is set and the command
-       line option is also given, both regular expressions
-       will be used.
-
++
+If the ignore-paths config key is set and the command line option is
+also given, both regular expressions will be used.
++
 Examples:
++
+--
+Skip "doc*" directory for every fetch;;
++
+------------------------------------------------------------------------
+--ignore-paths="^doc"
+------------------------------------------------------------------------
 
-       --ignore-paths="^doc" - skip "doc*" directory for every
-           fetch.
+Skip "branches" and "tags" of first level directories;;
++
+------------------------------------------------------------------------
+--ignore-paths="^[^/]+/(?:branches|tags)"
+------------------------------------------------------------------------
+--
 
-       --ignore-paths="^[^/]+/(?:branches|tags)" - skip
-           "branches" and "tags" of first level directories.
+--use-log-author;;
+       When retrieving svn commits into git (as part of fetch, rebase, or
+       dcommit operations), look for the first From: or Signed-off-by: line
+       in the log message and use that as the author string.
+--add-author-from;;
+       When committing to svn from git (as part of commit or dcommit
+       operations), if the existing log message doesn't already have a
+       From: or Signed-off-by: line, append a From: line based on the
+       git commit's author string.  If you use this, then --use-log-author
+       will retrieve a valid author string for all commits.
 
 'clone'::
        Runs 'init' and 'fetch'.  It will automatically create a
@@ -137,29 +158,29 @@ Examples:
        or if a second argument is passed; it will create a directory
        and work within that.  It accepts all arguments that the
        'init' and 'fetch' commands accept; with the exception of
-       '--fetch-all'.   After a repository is cloned, the 'fetch'
-       command will be able to update revisions without affecting
-       the working tree; and the 'rebase' command will be able
-       to update the working tree with the latest changes.
+       '--fetch-all' and '--parent'.  After a repository is cloned,
+       the 'fetch' command will be able to update revisions without
+       affecting the working tree; and the 'rebase' command will be
+       able to update the working tree with the latest changes.
 
 'rebase'::
        This fetches revisions from the SVN parent of the current HEAD
        and rebases the current (uncommitted to SVN) work against it.
-
-This works similarly to `svn update` or 'git-pull' except that
-it preserves linear history with 'git-rebase' instead of
-'git-merge' for ease of dcommitting with 'git-svn'.
-
-This accepts all options that 'git-svn fetch' and 'git-rebase'
++
+This works similarly to `svn update` or 'git pull' except that
+it preserves linear history with 'git rebase' instead of
+'git merge' for ease of dcommitting with 'git svn'.
++
+This accepts all options that 'git svn fetch' and 'git rebase'
 accept.  However, '--fetch-all' only fetches from the current
 [svn-remote], and not all [svn-remote] definitions.
-
-Like 'git-rebase'; this requires that the working tree be clean
++
+Like 'git rebase'; this requires that the working tree be clean
 and have no uncommitted changes.
 
 -l;;
 --local;;
-       Do not fetch remotely; only run 'git-rebase' against the
+       Do not fetch remotely; only run 'git rebase' against the
        last fetched commit from the upstream SVN.
 
 'dcommit'::
@@ -167,11 +188,12 @@ and have no uncommitted changes.
        repository, and then rebase or reset (depending on whether or
        not there is a diff between SVN and head).  This will create
        a revision in SVN for each commit in git.
-       It is recommended that you run 'git-svn' fetch and rebase (not
+       It is recommended that you run 'git svn' fetch and rebase (not
        pull or merge) your commits against the latest changes in the
        SVN repository.
-       An optional command-line argument may be specified as an
-       alternative to HEAD.
+       An optional revision or branch argument may be specified, and
+       causes 'git svn' to do all work on that revision/branch
+       instead of HEAD.
        This is advantageous over 'set-tree' (below) because it produces
        cleaner, more linear history.
 +
@@ -179,18 +201,17 @@ and have no uncommitted changes.
        After committing, do not rebase or reset.
 --commit-url <URL>;;
        Commit to this SVN URL (the full path).  This is intended to
-       allow existing git-svn repositories created with one transport
+       allow existing 'git svn' repositories created with one transport
        method (e.g. `svn://` or `http://` for anonymous read) to be
        reused if a user is later given access to an alternate transport
        method (e.g. `svn+ssh://` or `https://`) for commit.
-
++
+[verse]
 config key: svn-remote.<name>.commiturl
-
 config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
-
-       Using this option for any other purpose (don't ask)
-       is very strongly discouraged.
---
++
+Using this option for any other purpose (don't ask) is very strongly
+discouraged.
 
 'branch'::
        Create a branch in the SVN repository.
@@ -204,6 +225,20 @@ config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
        Create a tag by using the tags_subdir instead of the branches_subdir
        specified during git svn init.
 
+-d;;
+--destination;;
+       If more than one --branches (or --tags) option was given to the 'init'
+       or 'clone' command, you must provide the location of the branch (or
+       tag) you wish to create in the SVN repository.  The value of this
+       option must match one of the paths specified by a --branches (or
+       --tags) option.  You can see these paths with the commands
++
+       git config --get-all svn-remote.<name>.branches
+       git config --get-all svn-remote.<name>.tags
++
+where <name> is the name of the SVN repository as specified by the -R option to
+'init' (or "svn" by default).
+
 'tag'::
        Create a tag in the SVN repository. This is a shorthand for
        'branch -t'.
@@ -215,10 +250,12 @@ config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
 The following features from `svn log' are supported:
 +
 --
+-r <n>[:<n>];;
 --revision=<n>[:<n>];;
        is supported, non-numeric args are not:
        HEAD, NEXT, BASE, PREV, etc ...
--v/--verbose;;
+-v;;
+--verbose;;
        it's not completely compatible with the --verbose
        output in svn log, but reasonably close.
 --limit=<n>;;
@@ -241,7 +278,7 @@ NOTE: SVN itself only stores times in UTC and nothing else. The regular svn
 client converts the UTC time to the local time (or based on the TZ=
 environment). This command has the same behaviour.
 +
-Any other arguments are passed directly to 'git-log'
+Any other arguments are passed directly to 'git log'
 
 'blame'::
        Show what revision and author last modified each line of a file. The
@@ -249,15 +286,14 @@ Any other arguments are passed directly to 'git-log'
        `svn blame' by default. Like the SVN blame command,
        local uncommitted changes in the working copy are ignored;
        the version of the file in the HEAD revision is annotated. Unknown
-       arguments are passed directly to 'git-blame'.
+       arguments are passed directly to 'git blame'.
 +
 --git-format;;
-       Produce output in the same format as 'git-blame', but with
+       Produce output in the same format as 'git blame', but with
        SVN revision numbers instead of git commit hashes. In this mode,
        changes that haven't been committed to SVN (including local
        working-copy edits) are shown as revision 0.
 
---
 'find-rev'::
        When given an SVN revision number of the form 'rN', returns the
        corresponding git commit hash (this can optionally be followed by a
@@ -271,7 +307,7 @@ Any other arguments are passed directly to 'git-log'
        absolutely no attempts to do patching when committing to SVN, it
        simply overwrites files with those specified in the tree or
        commit.  All merging is assumed to have taken place
-       independently of 'git-svn' functions.
+       independently of 'git svn' functions.
 
 'create-ignore'::
        Recursively finds the svn:ignore property on directories and
@@ -286,12 +322,12 @@ Any other arguments are passed directly to 'git-log'
 
 'commit-diff'::
        Commits the diff of two tree-ish arguments from the
-       command-line.  This command does not rely on being inside an `git-svn
+       command-line.  This command does not rely on being inside an `git svn
        init`-ed repository.  This command takes three arguments, (a) the
        original tree to diff against, (b) the new tree result, (c) the
        URL of the target Subversion repository.  The final argument
-       (URL) may be omitted if you are working from a 'git-svn'-aware
-       repository (that has been `init`-ed with 'git-svn').
+       (URL) may be omitted if you are working from a 'git svn'-aware
+       repository (that has been `init`-ed with 'git svn').
        The -r<revision> option is required for this.
 
 'info'::
@@ -313,108 +349,170 @@ Any other arguments are passed directly to 'git-log'
        Shows the Subversion externals.  Use -r/--revision to specify a
        specific revision.
 
---
+'gc'::
+       Compress $GIT_DIR/svn/<refname>/unhandled.log files in .git/svn
+       and remove $GIT_DIR/svn/<refname>index files in .git/svn.
+
+'reset'::
+       Undoes the effects of 'fetch' back to the specified revision.
+       This allows you to re-'fetch' an SVN revision.  Normally the
+       contents of an SVN revision should never change and 'reset'
+       should not be necessary.  However, if SVN permissions change,
+       or if you alter your --ignore-paths option, a 'fetch' may fail
+       with "not found in commit" (file not previously visible) or
+       "checksum mismatch" (missed a modification).  If the problem
+       file cannot be ignored forever (with --ignore-paths) the only
+       way to repair the repo is to use 'reset'.
++
+Only the rev_map and refs/remotes/git-svn are changed.  Follow 'reset'
+with a 'fetch' and then 'git reset' or 'git rebase' to move local
+branches onto the new tree.
+
+-r <n>;;
+--revision=<n>;;
+       Specify the most recent revision to keep.  All later revisions
+       are discarded.
+-p;;
+--parent;;
+       Discard the specified revision as well, keeping the nearest
+       parent instead.
+Example:;;
+Assume you have local changes in "master", but you need to refetch "r2".
++
+------------
+    r1---r2---r3 remotes/git-svn
+                \
+                 A---B master
+------------
++
+Fix the ignore-paths or SVN permissions problem that caused "r2" to
+be incomplete in the first place.  Then:
++
+[verse]
+git svn reset -r2 -p
+git svn fetch
++
+------------
+    r1---r2'--r3' remotes/git-svn
+      \
+       r2---r3---A---B master
+------------
++
+Then fixup "master" with 'git rebase'.
+Do NOT use 'git merge' or your history will not be compatible with a
+future 'dcommit'!
++
+[verse]
+git rebase --onto remotes/git-svn A^ master
++
+------------
+    r1---r2'--r3' remotes/git-svn
+                \
+                 A'--B' master
+------------
 
 OPTIONS
 -------
---
 
 --shared[={false|true|umask|group|all|world|everybody}]::
 --template=<template_directory>::
        Only used with the 'init' command.
-       These are passed directly to 'git-init'.
+       These are passed directly to 'git init'.
 
 -r <ARG>::
 --revision <ARG>::
-
-Used with the 'fetch' command.
-
+          Used with the 'fetch' command.
++
 This allows revision ranges for partial/cauterized history
 to be supported.  $NUMBER, $NUMBER1:$NUMBER2 (numeric ranges),
 $NUMBER:HEAD, and BASE:$NUMBER are all supported.
-
++
 This can allow you to make partial mirrors when running fetch;
 but is generally not recommended because history will be skipped
 and lost.
 
 -::
 --stdin::
-
-Only used with the 'set-tree' command.
-
+       Only used with the 'set-tree' command.
++
 Read a list of commits from stdin and commit them in reverse
 order.  Only the leading sha1 is read from each line, so
-'git-rev-list --pretty=oneline' output can be used.
+'git rev-list --pretty=oneline' output can be used.
 
 --rmdir::
-
-Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
-
+       Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
++
 Remove directories from the SVN tree if there are no files left
 behind.  SVN can version empty directories, and they are not
 removed by default if there are no files left in them.  git
 cannot version empty directories.  Enabling this flag will make
 the commit to SVN act like git.
-
++
+[verse]
 config key: svn.rmdir
 
 -e::
 --edit::
-
-Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
-
+       Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
++
 Edit the commit message before committing to SVN.  This is off by
 default for objects that are commits, and forced on when committing
 tree objects.
-
++
+[verse]
 config key: svn.edit
 
 -l<num>::
 --find-copies-harder::
-
-Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
-
-They are both passed directly to 'git-diff-tree'; see
+       Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
++
+They are both passed directly to 'git diff-tree'; see
 linkgit:git-diff-tree[1] for more information.
-
++
 [verse]
 config key: svn.l
 config key: svn.findcopiesharder
 
 -A<filename>::
 --authors-file=<filename>::
-
-Syntax is compatible with the file used by 'git-cvsimport':
-
+       Syntax is compatible with the file used by 'git cvsimport':
++
 ------------------------------------------------------------------------
        loginname = Joe User <user@example.com>
 ------------------------------------------------------------------------
-
-If this option is specified and 'git-svn' encounters an SVN
-committer name that does not exist in the authors-file, 'git-svn'
++
+If this option is specified and 'git svn' encounters an SVN
+committer name that does not exist in the authors-file, 'git svn'
 will abort operation. The user will then have to add the
-appropriate entry.  Re-running the previous 'git-svn' command
+appropriate entry.  Re-running the previous 'git svn' command
 after the authors-file is modified should continue operation.
-
++
+[verse]
 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
+       Make 'git svn' less verbose. Specify a second time to make it
        even less verbose.
 
 --repack[=<n>]::
 --repack-flags=<flags>::
-
-These should help keep disk usage sane for large fetches
-with many revisions.
-
+       These should help keep disk usage sane for large fetches with
+       many revisions.
++
 --repack takes an optional argument for the number of revisions
 to fetch before repacking.  This defaults to repacking every
 1000 commits fetched if no argument is specified.
-
---repack-flags are passed directly to 'git-repack'.
-
++
+--repack-flags are passed directly to 'git repack'.
++
 [verse]
 config key: svn.repack
 config key: svn.repackflags
@@ -423,41 +521,36 @@ config key: svn.repackflags
 --merge::
 -s<strategy>::
 --strategy=<strategy>::
-
-These are only used with the 'dcommit' and 'rebase' commands.
-
-Passed directly to 'git-rebase' when using 'dcommit' if a
-'git-reset' cannot be used (see 'dcommit').
+       These are only used with the 'dcommit' and 'rebase' commands.
++
+Passed directly to 'git rebase' when using 'dcommit' if a
+'git reset' cannot be used (see 'dcommit').
 
 -n::
 --dry-run::
-
-This can be used with the 'dcommit', 'rebase', 'branch' and 'tag'
-commands.
-
+       This can be used with the 'dcommit', 'rebase', 'branch' and
+       'tag' commands.
++
 For 'dcommit', print out the series of git arguments that would show
 which diffs would be committed to SVN.
-
++
 For 'rebase', display the local branch associated with the upstream svn
 repository associated with the current branch and the URL of svn
 repository that will be fetched from.
-
++
 For 'branch' and 'tag', display the urls that will be used for copying when
 creating the branch or tag.
 
---
 
 ADVANCED OPTIONS
 ----------------
---
 
 -i<GIT_SVN_ID>::
 --id <GIT_SVN_ID>::
-
-This sets GIT_SVN_ID (instead of using the environment).  This
-allows the user to override the default refname to fetch from
-when tracking a single URL.  The 'log' and 'dcommit' commands
-no longer require this switch as an argument.
+       This sets GIT_SVN_ID (instead of using the environment).  This
+       allows the user to override the default refname to fetch from
+       when tracking a single URL.  The 'log' and 'dcommit' commands
+       no longer require this switch as an argument.
 
 -R<remote name>::
 --svn-remote <remote name>::
@@ -471,33 +564,30 @@ no longer require this switch as an argument.
        started tracking a branch and never tracked the trunk it was
        descended from. This feature is enabled by default, use
        --no-follow-parent to disable it.
-
++
+[verse]
 config key: svn.followparent
 
---
 CONFIG FILE-ONLY OPTIONS
 ------------------------
---
 
 svn.noMetadata::
 svn-remote.<name>.noMetadata::
-
-This gets rid of the 'git-svn-id:' lines at the end of every commit.
-
-If you lose your .git/svn/git-svn/.rev_db file, 'git-svn' will not
+       This gets rid of the 'git-svn-id:' lines at the end of every commit.
++
+If you lose your .git/svn/git-svn/.rev_db file, 'git svn' will not
 be able to rebuild it and you won't be able to fetch again,
 either.  This is fine for one-shot imports.
-
-The 'git-svn log' command will not work on repositories using
++
+The 'git svn log' command will not work on repositories using
 this, either.  Using this conflicts with the 'useSvmProps'
 option for (hopefully) obvious reasons.
 
 svn.useSvmProps::
 svn-remote.<name>.useSvmProps::
-
-This allows 'git-svn' to re-map repository URLs and UUIDs from
-mirrors created using SVN::Mirror (or svk) for metadata.
-
+       This allows 'git svn' to re-map repository URLs and UUIDs from
+       mirrors created using SVN::Mirror (or svk) for metadata.
++
 If an SVN revision has a property, "svm:headrev", it is likely
 that the revision was created by SVN::Mirror (also used by SVK).
 The property contains a repository UUID and a revision.  We want
@@ -514,23 +604,22 @@ svn-remote.<name>.useSvnsyncprops::
 
 svn-remote.<name>.rewriteRoot::
        This allows users to create repositories from alternate
-       URLs.  For example, an administrator could run 'git-svn' on the
+       URLs.  For example, an administrator could run 'git svn' on the
        server locally (accessing via file://) but wish to distribute
        the repository with a public http:// or svn:// URL in the
        metadata so users of it will see the public URL.
 
 svn.brokenSymlinkWorkaround::
-This disables potentially expensive checks to workaround broken symlinks
-checked into SVN by broken clients.  Set this option to "false" if you
-track a SVN repository with many empty blobs that are not symlinks.
-This option may be changed while "git-svn" is running and take effect on
-the next revision fetched.  If unset, git-svn assumes this option to be
-"true".
-
---
+       This disables potentially expensive checks to workaround
+       broken symlinks checked into SVN by broken clients.  Set this
+       option to "false" if you track a SVN repository with many
+       empty blobs that are not symlinks.  This option may be changed
+       while 'git svn' is running and take effect on the next
+       revision fetched.  If unset, 'git svn' assumes this option to
+       be "true".
 
 Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps
-options all affect the metadata generated and used by 'git-svn'; they
+options all affect the metadata generated and used by 'git svn'; they
 *must* be set in the configuration file before any history is imported
 and these settings should never be changed once they are set.
 
@@ -548,7 +637,7 @@ Tracking and contributing to the trunk of a Subversion-managed project:
        git svn clone http://svn.example.com/project/trunk
 # Enter the newly cloned directory:
        cd trunk
-# You should be on master branch, double-check with git-branch
+# You should be on master branch, double-check with 'git branch'
        git branch
 # Do some work and commit locally to git:
        git commit ...
@@ -579,12 +668,12 @@ Tracking and contributing to an entire Subversion-managed project
 # of dcommit/rebase/show-ignore should be the same as above.
 ------------------------------------------------------------------------
 
-The initial 'git-svn clone' can be quite time-consuming
+The initial 'git svn clone' can be quite time-consuming
 (especially for large Subversion repositories). If multiple
 people (or one person with multiple machines) want to use
-'git-svn' to interact with the same Subversion repository, you can
-do the initial 'git-svn clone' to a repository on a server and
-have each person clone that repository with 'git-clone':
+'git svn' to interact with the same Subversion repository, you can
+do the initial 'git svn clone' to a repository on a server and
+have each person clone that repository with 'git clone':
 
 ------------------------------------------------------------------------
 # Do the initial import on a server
@@ -598,7 +687,7 @@ have each person clone that repository with 'git-clone':
        git fetch
 # Create a local branch from one of the branches just fetched
        git checkout -b master FETCH_HEAD
-# Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server)
+# Initialize 'git svn' locally (be sure to use the same URL and -T/-b/-t options as were used on server)
        git svn init http://svn.example.com/project
 # Pull the latest changes from Subversion
        git svn rebase
@@ -607,7 +696,7 @@ have each person clone that repository with 'git-clone':
 REBASE VS. PULL/MERGE
 ---------------------
 
-Originally, 'git-svn' recommended that the 'remotes/git-svn' branch be
+Originally, 'git svn' recommended that the 'remotes/git-svn' branch be
 pulled or merged from.  This is because the author favored
 `git svn set-tree B` to commit a single head rather than the
 `git svn set-tree A..B` notation to commit multiple commits.
@@ -615,14 +704,14 @@ pulled or merged from.  This is because the author favored
 If you use `git svn set-tree A..B` to commit several diffs and you do
 not have the latest remotes/git-svn merged into my-branch, you should
 use `git svn rebase` to update your work branch instead of `git pull` or
-`git merge`.  `pull`/`merge' can cause non-linear history to be flattened
+`git merge`.  `pull`/`merge` can cause non-linear history to be flattened
 when committing into SVN, which can lead to merge commits reversing
 previous commits in SVN.
 
 DESIGN PHILOSOPHY
 -----------------
 Merge tracking in Subversion is lacking and doing branched development
-with Subversion can be cumbersome as a result.  While 'git-svn' can track
+with Subversion can be cumbersome as a result.  While 'git svn' can track
 copy history (including branches and tags) for repositories adopting a
 standard layout, it cannot yet represent merge history that happened
 inside git back upstream to SVN users.  Therefore it is advised that
@@ -633,25 +722,25 @@ CAVEATS
 -------
 
 For the sake of simplicity and interoperating with a less-capable system
-(SVN), it is recommended that all 'git-svn' users clone, fetch and dcommit
-directly from the SVN server, and avoid all 'git-clone'/'pull'/'merge'/'push'
+(SVN), it is recommended that all 'git svn' users clone, fetch and dcommit
+directly from the SVN server, and avoid all 'git clone'/'pull'/'merge'/'push'
 operations between git repositories and branches.  The recommended
 method of exchanging code between git branches and users is
-'git-format-patch' and 'git-am', or just 'dcommit'ing to the SVN repository.
+'git format-patch' and 'git am', or just 'dcommit'ing to the SVN repository.
 
-Running 'git-merge' or 'git-pull' is NOT recommended on a branch you
+Running 'git merge' or 'git pull' is NOT recommended on a branch you
 plan to 'dcommit' from.  Subversion does not represent merges in any
 reasonable or useful fashion; so users using Subversion cannot see any
 merges you've made.  Furthermore, if you merge or pull from a git branch
 that is a mirror of an SVN branch, 'dcommit' may commit to the wrong
 branch.
 
-'git-clone' does not clone branches under the refs/remotes/ hierarchy or
-any 'git-svn' metadata, or config.  So repositories created and managed with
-using 'git-svn' should use 'rsync' for cloning, if cloning is to be done
+'git clone' does not clone branches under the refs/remotes/ hierarchy or
+any 'git svn' metadata, or config.  So repositories created and managed with
+using 'git svn' should use 'rsync' for cloning, if cloning is to be done
 at all.
 
-Since 'dcommit' uses rebase internally, any git branches you 'git-push' to
+Since 'dcommit' uses rebase internally, any git branches you 'git push' to
 before 'dcommit' on will require forcing an overwrite of the existing ref
 on the remote repository.  This is generally considered bad practice,
 see the linkgit:git-push[1] documentation for details.
@@ -661,6 +750,16 @@ already dcommitted.  It is considered bad practice to --amend commits
 you've already pushed to a remote repository for other users, and
 dcommit with SVN is analogous to that.
 
+When using multiple --branches or --tags, 'git svn' does not automatically
+handle name collisions (for example, if two branches from different paths have
+the same name, or if a branch and a tag have the same name).  In these cases,
+use 'init' to set up your git repository then, before your first 'fetch', edit
+the .git/config file so that the branches and tags are associated with
+different name spaces.  For example:
+
+       branches = stable/*:refs/remotes/svn/stable/*
+       branches = debug/*:refs/remotes/svn/debug/*
+
 BUGS
 ----
 
@@ -677,7 +776,7 @@ for git to detect them.
 CONFIGURATION
 -------------
 
-'git-svn' stores [svn-remote] configuration information in the
+'git svn' stores [svn-remote] configuration information in the
 repository .git/config file.  It is similar the core git
 [remote] sections except 'fetch' keys do not accept glob
 arguments; but they are instead handled by the 'branches'
@@ -698,7 +797,7 @@ Keep in mind that the '\*' (asterisk) wildcard of the local ref
 however the remote wildcard may be anywhere as long as it's an
 independent path component (surrounded by '/' or EOL).   This
 type of configuration is not automatically created by 'init' and
-should be manually entered with a text-editor or using 'git-config'.
+should be manually entered with a text-editor or using 'git config'.
 
 SEE ALSO
 --------
index 210fde03a12cd757769f81754e789a2a5934f02c..63925388073b17c3087d32813979513580adfa2a 100644 (file)
@@ -14,9 +14,9 @@ DESCRIPTION
 Given one argument, reads which branch head the given symbolic
 ref refers to and outputs its path, relative to the `.git/`
 directory.  Typically you would give `HEAD` as the <name>
-argument to see on which branch your working tree is on.
+argument to see which branch your working tree is on.
 
-Give two arguments, create or update a symbolic ref <name> to
+Given two arguments, creates or updates a symbolic ref <name> to
 point at the given branch <ref>.
 
 A symbolic ref is a regular file that stores a string that
index fa733214ab0259dec1c866e9cb629dd0e7b69f3a..299b04f726b2e7662fb7db64169726eec82a5c14 100644 (file)
@@ -10,14 +10,15 @@ SYNOPSIS
 --------
 [verse]
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
-       <name> [<commit> | <object>]
-'git tag' -d <name>...
+       <tagname> [<commit> | <object>]
+'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
-'git tag' -v <name>...
+'git tag' -v <tagname>...
 
 DESCRIPTION
 -----------
-Adds a 'tag' reference in `.git/refs/tags/`
+
+Adds a tag reference in `.git/refs/tags/`.
 
 Unless `-f` is given, the tag must not yet exist in
 `.git/refs/tags/` directory.
@@ -50,6 +51,7 @@ OPTIONS
        Make a GPG-signed tag, using the given key
 
 -f::
+--force::
        Replace an existing tag with the given name (instead of failing)
 
 -d::
@@ -85,6 +87,12 @@ OPTIONS
        Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
        is given.
 
+<tagname>::
+       The name of the tag to create, delete, or describe.
+       The new tag name must pass all checks defined by
+       linkgit:git-check-ref-format[1].  Some of these checks
+       may restrict the characters allowed in a tag name.
+
 CONFIGURATION
 -------------
 By default, 'git-tag' in sign-with-default mode (-s) will use your
@@ -249,6 +257,10 @@ $ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
 ------------
 
 
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1].
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>,
index b8e49dce4a19a4d7083459468f27c273c1d91fea..63f3b5c7425cd0b76a6931d92872424310f5fbce 100644 (file)
@@ -20,6 +20,8 @@ The UI for the protocol is on the 'git-fetch-pack' side, and the
 program pair is meant to be used to pull updates from a remote
 repository.  For push operations, see 'git-send-pack'.
 
+After finishing the operation successfully, `post-upload-pack`
+hook is called (see linkgit:githooks[5]).
 
 OPTIONS
 -------
index c8611632d1d501d57eb7000de0ec3c3b36810b80..97f7f9165eca2311e4806e9cab8ef18c8c94f7d5 100644 (file)
@@ -8,7 +8,7 @@ git-verify-pack - Validate packed git archive files
 
 SYNOPSIS
 --------
-'git verify-pack' [-v] [--] <pack>.idx ...
+'git verify-pack' [-v|--verbose] [--] <pack>.idx ...
 
 
 DESCRIPTION
@@ -23,8 +23,15 @@ OPTIONS
        The idx files to verify.
 
 -v::
+--verbose::
        After verifying the pack, show list of objects contained
-       in the pack.
+       in the pack and a histogram of delta chain length.
+
+-s::
+--stat-only::
+       Do not verify the pack contents; only show the histogram of delta
+       chain length.  With `--verbose`, list of objects is also shown.
+
 \--::
        Do not interpret any more arguments as options.
 
index 26d3850e7317c22dcf0999e0c4a6afe9a5ea2e03..c8899d528adfe37459b9301e16faab90e7a7e63d 100644 (file)
@@ -12,7 +12,8 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Creates a tree object using the current index.
+Creates a tree object using the current index. The name of the new
+tree object is printed to standard output.
 
 The index must be in a fully merged state.
 
index 9d8f236fe84cd02550b8cf08e835b2926e6c45f0..d11c5c16510025b93cf88bc97e95aa008169434c 100644 (file)
@@ -43,7 +43,29 @@ 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.5/git.html[documentation for release 1.6.5]
+
+* release notes for
+  link:RelNotes-1.6.5.1.txt[1.6.5.1],
+  link:RelNotes-1.6.5.txt[1.6.5].
+
+* link:v1.6.4.4/git.html[documentation for release 1.6.4.4]
+
+* release notes for
+  link:RelNotes-1.6.4.4.txt[1.6.4.4],
+  link:RelNotes-1.6.4.3.txt[1.6.4.3],
+  link:RelNotes-1.6.4.2.txt[1.6.4.2],
+  link:RelNotes-1.6.4.1.txt[1.6.4.1],
+  link:RelNotes-1.6.4.txt[1.6.4].
+
+* link:v1.6.3.4/git.html[documentation for release 1.6.3.4]
+
+* release notes for
+  link:RelNotes-1.6.3.4.txt[1.6.3.4],
+  link:RelNotes-1.6.3.3.txt[1.6.3.3],
+  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],
@@ -227,6 +249,8 @@ The link:user-manual.html#git-concepts[git concepts chapter of the
 user-manual] and linkgit:gitcore-tutorial[7] both provide
 introductions to the underlying git architecture.
 
+See linkgit:gitworkflows[7] for an overview of recommended workflows.
+
 See also the link:howto-index.html[howto] documents for some useful
 examples.
 
@@ -313,7 +337,7 @@ Synching repositories
 
 include::cmds-synchingrepositories.txt[]
 
-The following are helper programs used by the above; end users
+The following are helper commands used by the above; end users
 typically do not use them directly.
 
 include::cmds-synchelpers.txt[]
@@ -644,7 +668,8 @@ SEE ALSO
 linkgit:gittutorial[7], linkgit:gittutorial-2[7],
 link:everyday.html[Everyday Git], linkgit:gitcvs-migration[7],
 linkgit:gitglossary[7], linkgit:gitcore-tutorial[7],
-linkgit:gitcli[7], link:user-manual.html[The Git User's Manual]
+linkgit:gitcli[7], link:user-manual.html[The Git User's Manual],
+linkgit:gitworkflows[7]
 
 GIT
 ---
index aaa073efc80522a649f17d60127aae8cc85b0b3b..1195e83b6e65b5cf7926c973c730ffe19ff92202 100644 (file)
@@ -404,7 +404,7 @@ Performing a three-way merge
 
 The attribute `merge` affects how three versions of a file is
 merged when a file-level merge is necessary during `git merge`,
-and other programs such as `git revert` and `git cherry-pick`.
+and other commands such as `git revert` and `git cherry-pick`.
 
 Set::
 
index be39ed7c1509158867998ce8fb77341242ecdf79..6928724a05f304c430e8d7f60cc35d22e4873bca 100644 (file)
@@ -81,7 +81,7 @@ couple of magic command line options:
 +
 ---------------------------------------------
 $ git describe -h
-usage: git-describe [options] <committish>*
+usage: git describe [options] <committish>*
 
     --contains            find the tag that comes after the commit
     --debug               debug search strategy on stderr
index 7ba5e589d7e824c526482c9707a5c26ac730cc9e..b3640c4e64494689841e598cf17741ea8620af12 100644 (file)
@@ -12,7 +12,7 @@ git *
 DESCRIPTION
 -----------
 
-This tutorial explains how to use the "core" git programs to set up and
+This tutorial explains how to use the "core" git commands to set up and
 work with a git repository.
 
 If you just need to use git as a revision control system you may prefer
@@ -1328,7 +1328,7 @@ into it later. Obviously, this repository creation needs to be
 done only once.
 
 [NOTE]
-'git-push' uses a pair of programs,
+'git-push' uses a pair of commands,
 'git-send-pack' on your local machine, and 'git-receive-pack'
 on the remote machine. The communication between the two over
 the network internally uses an SSH connection.
index 1c736738ccc91b6929226a27e02716221ec3ef05..06e0f315c3bb5bb6ef3320b8b1922b76480f9646 100644 (file)
@@ -26,8 +26,11 @@ executable by default.
 
 This document describes the currently defined hooks.
 
+HOOKS
+-----
+
 applypatch-msg
---------------
+~~~~~~~~~~~~~~
 
 This hook is invoked by 'git-am' script.  It takes a single
 parameter, the name of the file that holds the proposed commit
@@ -43,7 +46,7 @@ The default 'applypatch-msg' hook, when enabled, runs the
 'commit-msg' hook, if the latter is enabled.
 
 pre-applypatch
---------------
+~~~~~~~~~~~~~~
 
 This hook is invoked by 'git-am'.  It takes no parameter, and is
 invoked after the patch is applied, but before a commit is made.
@@ -58,7 +61,7 @@ The default 'pre-applypatch' hook, when enabled, runs the
 'pre-commit' hook, if the latter is enabled.
 
 post-applypatch
----------------
+~~~~~~~~~~~~~~~
 
 This hook is invoked by 'git-am'.  It takes no parameter,
 and is invoked after the patch is applied and a commit is made.
@@ -67,7 +70,7 @@ This hook is meant primarily for notification, and cannot affect
 the outcome of 'git-am'.
 
 pre-commit
-----------
+~~~~~~~~~~
 
 This hook is invoked by 'git-commit', and can be bypassed
 with `\--no-verify` option.  It takes no parameter, and is
@@ -84,7 +87,7 @@ variable `GIT_EDITOR=:` if the command will not bring up an editor
 to modify the commit message.
 
 prepare-commit-msg
-------------------
+~~~~~~~~~~~~~~~~~~
 
 This hook is invoked by 'git-commit' right after preparing the
 default log message, and before the editor is started.
@@ -109,7 +112,7 @@ The sample `prepare-commit-msg` hook that comes with git comments
 out the `Conflicts:` part of a merge's commit message.
 
 commit-msg
-----------
+~~~~~~~~~~
 
 This hook is invoked by 'git-commit', and can be bypassed
 with `\--no-verify` option.  It takes a single parameter, the
@@ -126,7 +129,7 @@ The default 'commit-msg' hook, when enabled, detects duplicate
 "Signed-off-by" lines, and aborts the commit if one is found.
 
 post-commit
------------
+~~~~~~~~~~~
 
 This hook is invoked by 'git-commit'.  It takes no
 parameter, and is invoked after a commit is made.
@@ -135,14 +138,14 @@ This hook is meant primarily for notification, and cannot affect
 the outcome of 'git-commit'.
 
 pre-rebase
-----------
+~~~~~~~~~~
 
 This hook is called by 'git-rebase' and can be used to prevent a branch
 from getting rebased.
 
 
 post-checkout
------------
+~~~~~~~~~~~~~
 
 This hook is invoked when a 'git-checkout' is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
@@ -160,7 +163,7 @@ differences from the previous HEAD if different, or set working dir metadata
 properties.
 
 post-merge
------------
+~~~~~~~~~~
 
 This hook is invoked by 'git-merge', which happens when a 'git-pull'
 is done on a local repository.  The hook takes a single parameter, a status
@@ -175,7 +178,7 @@ for an example of how to do this.
 
 [[pre-receive]]
 pre-receive
------------
+~~~~~~~~~~~
 
 This hook is invoked by 'git-receive-pack' on the remote repository,
 which happens when a 'git-push' is done on a local repository.
@@ -204,7 +207,7 @@ for the user.
 
 [[update]]
 update
-------
+~~~~~~
 
 This hook is invoked by 'git-receive-pack' on the remote repository,
 which happens when a 'git-push' is done on a local repository.
@@ -242,12 +245,12 @@ Both standard output and standard error output are forwarded to
 for the user.
 
 The default 'update' hook, when enabled--and with
-`hooks.allowunannotated` config option turned on--prevents
+`hooks.allowunannotated` config option unset or set to false--prevents
 unannotated tags to be pushed.
 
 [[post-receive]]
 post-receive
-------------
+~~~~~~~~~~~~
 
 This hook is invoked by 'git-receive-pack' on the remote repository,
 which happens when a 'git-push' is done on a local repository.
@@ -277,7 +280,7 @@ emails.
 
 [[post-update]]
 post-update
------------
+~~~~~~~~~~~
 
 This hook is invoked by 'git-receive-pack' on the remote repository,
 which happens when a 'git-push' is done on a local repository.
@@ -307,8 +310,37 @@ Both standard output and standard error output are forwarded to
 'git-send-pack' on the other end, so you can simply `echo` messages
 for the user.
 
+post-upload-pack
+----------------
+
+After upload-pack successfully finishes its operation, this hook is called
+for logging purposes.
+
+The hook is passed various pieces of information, one per line, from its
+standard input.  Currently the following items can be fed to the hook, but
+more types of information may be added in the future:
+
+want SHA-1::
+    40-byte hexadecimal object name the client asked to include in the
+    resulting pack.  Can occur one or more times in the input.
+
+have SHA-1::
+    40-byte hexadecimal object name the client asked to exclude from
+    the resulting pack, claiming to have them already.  Can occur zero
+    or more times in the input.
+
+time float::
+    Number of seconds spent for creating the packfile.
+
+size decimal::
+    Size of the resulting packfile in bytes.
+
+kind string:
+    Either "clone" (when the client did not give us any "have", and asked
+    for all our refs with "want"), or "fetch" (otherwise).
+
 pre-auto-gc
------------
+~~~~~~~~~~~
 
 This hook is invoked by 'git-gc --auto'. It takes no parameter, and
 exiting with non-zero status from this script causes the 'git-gc --auto'
index d1a17e2625890245341a2099cc2b058e63564da2..5daf750d1942f3b97844b4ef378daf9346cb46d4 100644 (file)
@@ -30,6 +30,17 @@ 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. If 'merge', the commit
+       specified in the superproject will be merged into the current branch
+       in the submodule.
+       This config option is overridden if 'git submodule update' is given
+       the '--merge' or '--rebase' options.
+
 
 EXAMPLES
 --------
index c5d5596d895755d69c8060bc38a800d7c70eda26..cf0689cfeb8b5122f8fa20345d9f5307fee1cd6c 100644 (file)
@@ -332,11 +332,11 @@ alice$ git log -p HEAD..FETCH_HEAD
 ------------------------------------------------
 
 This operation is safe even if Alice has uncommitted local changes.
-The range notation HEAD..FETCH_HEAD" means "show everything that is reachable
-from the FETCH_HEAD but exclude anything that is reachable from HEAD.
+The range notation "HEAD..FETCH_HEAD" means "show everything that is reachable
+from the FETCH_HEAD but exclude anything that is reachable from HEAD".
 Alice already knows everything that leads to her current state (HEAD),
-and reviewing what Bob has in his state (FETCH_HEAD) that she has not
-seen with this command
+and reviews what Bob has in his state (FETCH_HEAD) that she has not
+seen with this command.
 
 If Alice wants to visualize what Bob did since their histories forked
 she can issue the following command:
@@ -375,9 +375,9 @@ it easier:
 alice$ git remote add bob /home/bob/myrepo
 ------------------------------------------------
 
-With this, Alice can perform the first part of the "pull" operation alone using the
-'git-fetch' command without merging them with her own branch,
-using:
+With this, Alice can perform the first part of the "pull" operation
+alone using the 'git-fetch' command without merging them with her own
+branch, using:
 
 -------------------------------------
 alice$ git fetch bob
@@ -566,22 +566,22 @@ $ git log v2.5.. Makefile       # commits since v2.5 which modify
 
 You can also give 'git-log' a "range" of commits where the first is not
 necessarily an ancestor of the second; for example, if the tips of
-the branches "stable-release" and "master" diverged from a common
+the branches "stable" and "master" diverged from a common
 commit some time ago, then
 
 -------------------------------------
-$ git log stable..experimental
+$ git log stable..master
 -------------------------------------
 
-will list commits made in the experimental branch but not in the
+will list commits made in the master branch but not in the
 stable branch, while
 
 -------------------------------------
-$ git log experimental..stable
+$ git log master..stable
 -------------------------------------
 
 will show the list of commits made on the stable branch but not
-the experimental branch.
+the master branch.
 
 The 'git-log' command has a weakness: it must present commits in a
 list.  When the history has lines of development that diverged and
@@ -650,6 +650,9 @@ digressions that may be interesting at this point are:
     smart enough to perform a close-to-optimal search even in the
     case of complex non-linear history with lots of merged branches.
 
+  * linkgit:gitworkflows[7]: Gives an overview of recommended
+    workflows.
+
   * link:everyday.html[Everyday GIT with 20 Commands Or So]
 
   * linkgit:gitcvs-migration[7]: Git for CVS users.
@@ -661,6 +664,7 @@ linkgit:gitcvs-migration[7],
 linkgit:gitcore-tutorial[7],
 linkgit:gitglossary[7],
 linkgit:git-help[1],
+linkgit:gitworkflows[7],
 link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
index 572374f7a6ff17dd40ad687a2fda24d53f7d730b..43d84d15e921435ba4629a7d8c18021d16f89223 100644 (file)
@@ -456,6 +456,6 @@ This commit is referred to as a "merge commit", or sometimes just a
        of 'A' is 'origin/B' sometimes we say "'A' is tracking 'origin/B'".
 
 [[def_working_tree]]working tree::
-       The tree of actual checked out files.  The working tree is
-       normally equal to the <<def_HEAD,HEAD>> plus any local changes
-       that you have made but not yet committed.
+       The tree of actual checked out files.  The working tree normally
+       contains the contents of the <<def_HEAD,HEAD>> commit's tree,
+       plus any local changes that you have made but not yet committed.
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 637b53f898829af98153d60953434fa30e3df179..adadf8e4bf309a2fd2ec8efbeff09b410ca7b041 100644 (file)
@@ -39,7 +39,8 @@
 
 --squash::
        Produce the working tree and index state as if a real
-       merge happened, but do not actually make a commit or
+       merge happened (except for the merge information),
+       but do not actually make a commit or
        move the `HEAD`, nor record `$GIT_DIR/MERGE_HEAD` to
        cause the next `git commit` command to create a merge
        commit.  This allows you to create a single commit on
diff --git a/Documentation/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt
new file mode 100644 (file)
index 0000000..beba065
--- /dev/null
@@ -0,0 +1,675 @@
+gittutorial(7)
+==============
+
+NOME
+----
+gittutorial - Um tutorial de introdução ao git (para versão 1.5.1 ou mais nova)
+
+SINOPSE
+--------
+git *
+
+DESCRIÇÃO
+-----------
+
+Este tutorial explica como importar um novo projeto para o git,
+adicionar mudanças a ele, e compartilhar mudanças com outros
+desenvolvedores.
+
+Se, ao invés disso, você está interessado primariamente em usar git para
+obter um projeto, por exemplo, para testar a última versão, você pode
+preferir começar com os primeiros dois capítulos de
+link:user-manual.html[O Manual do Usuário Git].
+
+Primeiro, note que você pode obter documentação para um comando como
+`git log --graph` com:
+
+------------------------------------------------
+$ man git-log
+------------------------------------------------
+
+ou:
+
+------------------------------------------------
+$ git help log
+------------------------------------------------
+
+Com a última forma, você pode usar o visualizador de manual de sua
+escolha; veja linkgit:git-help[1] para maior informação.
+
+É uma boa idéia informar ao git seu nome e endereço público de email
+antes de fazer qualquer operação. A maneira mais fácil de fazê-lo é:
+
+------------------------------------------------
+$ git config --global user.name "Seu Nome Vem Aqui"
+$ git config --global user.email voce@seudominio.exemplo.com
+------------------------------------------------
+
+
+Importando um novo projeto
+-----------------------
+
+Assuma que você tem um tarball project.tar.gz com seu trabalho inicial.
+Você pode colocá-lo sob controle de revisão git da seguinte forma:
+
+------------------------------------------------
+$ tar xzf project.tar.gz
+$ cd project
+$ git init
+------------------------------------------------
+
+Git irá responder
+
+------------------------------------------------
+Initialized empty Git repository in .git/
+------------------------------------------------
+
+Agora que você iniciou seu diretório de trabalho, você deve ter notado que um
+novo diretório foi criado com o nome de ".git".
+
+A seguir, diga ao git para gravar um instantâneo do conteúdo de todos os
+arquivos sob o diretório atual (note o '.'), com 'git-add':
+
+------------------------------------------------
+$ git add .
+------------------------------------------------
+
+Este instantâneo está agora armazenado em uma área temporária que o git
+chama de "index" ou índice. Você pode armazenar permanentemente o
+conteúdo do índice no repositório com 'git-commit':
+
+------------------------------------------------
+$ git commit
+------------------------------------------------
+
+Isto vai te pedir por uma mensagem de commit. Você agora gravou sua
+primeira versão de seu projeto no git.
+
+Fazendo mudanças
+--------------
+
+Modifique alguns arquivos, e, então, adicione seu conteúdo atualizado ao
+índice:
+
+------------------------------------------------
+$ git add file1 file2 file3
+------------------------------------------------
+
+Você está agora pronto para fazer o commit. Você pode ver o que está
+para ser gravado usando 'git-diff' com a opção --cached:
+
+------------------------------------------------
+$ git diff --cached
+------------------------------------------------
+
+(Sem --cached, o comando 'git-diff' irá te mostrar quaisquer mudanças
+que você tenha feito mas ainda não adicionou ao índice.) Você também
+pode obter um breve sumário da situação com 'git-status':
+
+------------------------------------------------
+$ git status
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      modified:   file1
+#      modified:   file2
+#      modified:   file3
+#
+------------------------------------------------
+
+Se você precisar fazer qualquer outro ajuste, faça-o agora, e, então,
+adicione qualquer conteúdo modificado ao índice. Finalmente, grave suas
+mudanças com:
+
+------------------------------------------------
+$ git commit
+------------------------------------------------
+
+Ao executar esse comando, ele irá te pedir uma mensagem descrevendo a mudança,
+e, então, irá gravar a nova versão do projeto.
+
+Alternativamente, ao invés de executar 'git-add' antes, você pode usar
+
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
+
+o que irá automaticamente notar quaisquer arquivos modificados (mas não
+novos), adicioná-los ao índices, e gravar, tudo em um único passo.
+
+Uma nota em mensagens de commit: Apesar de não ser exigido, é uma boa
+idéia começar a mensagem com uma simples e curta (menos de 50
+caracteres) linha sumarizando a mudança, seguida de uma linha em branco
+e, então, uma descrição mais detalhada. Ferramentas que transformam
+commits em email, por exemplo, usam a primeira linha no campo de
+cabeçalho "Subject:" e o resto no corpo.
+
+Git rastreia conteúdo, não arquivos
+----------------------------
+
+Muitos sistemas de controle de revisão provêem um comando `add` que diz
+ao sistema para começar a rastrear mudanças em um novo arquivo. O
+comando `add` do git faz algo mais simples e mais poderoso: 'git-add' é
+usado tanto para arquivos novos e arquivos recentemente modificados, e
+em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena
+o conteúdo no índice, pronto para inclusão do próximo commit.
+
+Visualizando a história do projeto
+-----------------------
+
+Em qualquer ponto você pode visualizar a história das suas mudanças
+usando
+
+------------------------------------------------
+$ git log
+------------------------------------------------
+
+Se você também quiser ver a diferença completa a cada passo, use
+
+------------------------------------------------
+$ git log -p
+------------------------------------------------
+
+Geralmente, uma visão geral da mudança é útil para ter a sensação de
+cada passo
+
+------------------------------------------------
+$ git log --stat --summary
+------------------------------------------------
+
+Gerenciando "branches"/ramos
+-----------------
+
+Um simples repositório git pode manter múltiplos ramos de
+desenvolvimento. Para criar um novo ramo chamado "experimental", use
+
+------------------------------------------------
+$ git branch experimental
+------------------------------------------------
+
+Se você executar agora
+
+------------------------------------------------
+$ git branch
+------------------------------------------------
+
+você vai obter uma lista de todos os ramos existentes:
+
+------------------------------------------------
+  experimental
+* master
+------------------------------------------------
+
+O ramo "experimental" é o que você acaba de criar, e o ramo "master" é o
+ramo padrão que foi criado pra você automaticamente. O asterisco marca
+o ramo em que você está atualmente; digite
+
+------------------------------------------------
+$ git checkout experimental
+------------------------------------------------
+
+para mudar para o ramo experimental. Agora edite um arquivo, grave a
+mudança, e mude de volta para o ramo master:
+
+------------------------------------------------
+(edita arquivo)
+$ git commit -a
+$ git checkout master
+------------------------------------------------
+
+Verifique que a mudança que você fez não está mais visível, já que ela
+foi feita no ramo experimental e você está de volta ao ramo master.
+
+Você pode fazer uma mudança diferente no ramo master:
+
+------------------------------------------------
+(edit file)
+$ git commit -a
+------------------------------------------------
+
+neste ponto, os dois ramos divergiram, com diferentes mudanças feitas em
+cada um. Para unificar as mudanças feitas no experimental para o
+master, execute
+
+------------------------------------------------
+$ git merge experimental
+------------------------------------------------
+
+Se as mudanças não conflitarem, estará pronto. Se existirem conflitos,
+marcadores serão deixados nos arquivos problemáticos exibindo o
+conflito;
+
+------------------------------------------------
+$ git diff
+------------------------------------------------
+
+vai exibir isto. Após você editar os arquivos para resolver os
+conflitos,
+
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
+
+irá gravar o resultado da unificação. Finalmente,
+
+------------------------------------------------
+$ gitk
+------------------------------------------------
+
+vai mostrar uma bela representação gráfica da história resultante.
+
+Neste ponto você pode remover seu ramo experimental com
+
+------------------------------------------------
+$ git branch -d experimental
+------------------------------------------------
+
+Este comando garante que as mudanças no ramo experimental já estão no
+ramo atual.
+
+Se você desenvolve em um ramo ideia-louca, e se arrepende, você pode
+sempre remover o ramo com
+
+-------------------------------------
+$ git branch -D ideia-louca
+-------------------------------------
+
+Ramos são baratos e fáceis, então isto é uma boa maneira de experimentar
+alguma coisa.
+
+Usando git para colaboração
+---------------------------
+
+Suponha que Alice começou um novo projeto com um repositório git em
+/home/alice/project, e que Bob, que tem um diretório home na mesma
+máquina, quer contribuir.
+
+Bob começa com:
+
+------------------------------------------------
+bob$ git clone /home/alice/project myrepo
+------------------------------------------------
+
+Isso cria um novo diretório "myrepo" contendo um clone do repositório de
+Alice. O clone está no mesmo pé que o projeto original, possuindo sua
+própria cópia da história do projeto original.
+
+Bob então faz algumas mudanças e as grava:
+
+------------------------------------------------
+(editar arquivos)
+bob$ git commit -a
+(repetir conforme necessário)
+------------------------------------------------
+
+Quanto está pronto, ele diz a Alice para puxar as mudanças do
+repositório em /home/bob/myrepo. Ela o faz com:
+
+------------------------------------------------
+alice$ cd /home/alice/project
+alice$ git pull /home/bob/myrepo master
+------------------------------------------------
+
+Isto unifica as mudanças do ramo "master" do Bob ao ramo atual de Alice.
+Se Alice fez suas próprias mudanças no intervalo, ela, então, pode
+precisar corrigir manualmente quaisquer conflitos. (Note que o argumento
+"master" no comando acima é, de fato, desnecessário, já que é o padrão.)
+
+O comando "pull" executa, então, duas operações: ele obtém mudanças de
+um ramo remoto, e, então, as unifica no ramo atual.
+
+Note que, em geral, Alice gostaria que suas mudanças locais fossem
+gravadas antes de iniciar este "pull". Se o trabalho de Bob conflita
+com o que Alice fez desde que suas histórias se ramificaram, Alice irá
+usar seu diretório de trabalho e o índice para resolver conflitos, e
+mudanças locais existentes irão interferir com o processo de resolução
+de conflitos (git ainda irá realizar a obtenção mas irá se recusar a
+unificar --- Alice terá que se livrar de suas mudanças locais de alguma
+forma e puxar de novo quando isso acontecer).
+
+Alice pode espiar o que Bob fez sem unificar primeiro, usando o comando
+"fetch"; isto permite Alice inspecionar o que Bob fez, usando um símbolo
+especial "FETCH_HEAD", com o fim de determinar se ele tem alguma coisa
+que vale puxar, assim:
+
+------------------------------------------------
+alice$ git fetch /home/bob/myrepo master
+alice$ git log -p HEAD..FETCH_HEAD
+------------------------------------------------
+
+Esta operação é segura mesmo se Alice tem mudanças locais não gravadas.
+A notação de intervalo "HEAD..FETCH_HEAD" significa mostrar tudo que é
+alcançável de FETCH_HEAD mas exclua tudo o que é alcançável de HEAD.
+Alice já sabe tudo que leva a seu estado atual (HEAD), e revisa o que Bob
+tem em seu estado (FETCH_HEAD) que ela ainda não viu com esse comando.
+
+Se Alice quer visualizar o que Bob fez desde que suas histórias se
+ramificaram, ela pode disparar o seguinte comando:
+
+------------------------------------------------
+$ gitk HEAD..FETCH_HEAD
+------------------------------------------------
+
+Isto usa a mesma notação de intervalo que vimos antes com 'git log'.
+
+Alice pode querer ver o que ambos fizeram desde que ramificaram. Ela
+pode usar a forma com três pontos ao invés da forma com dois pontos:
+
+------------------------------------------------
+$ gitk HEAD...FETCH_HEAD
+------------------------------------------------
+
+Isto significa "mostre tudo que é alcançável de qualquer um deles, mas
+exclua tudo que é alcançável a partir de ambos".
+
+Por favor, note que essas notações de intervalo podem ser usadas tanto
+com gitk quanto com "git log".
+
+Após inspecionar o que Bob fez, se não há nada urgente, Alice pode
+decidir continuar trabalhando sem puxar de Bob. Se a história de Bob
+tem alguma coisa que Alice precisa imediatamente, Alice pode optar por
+separar seu trabalho em progresso primeiro, fazer um "pull", e, então,
+finalmente, retomar seu trabalho em progresso em cima da história
+resultante.
+
+Quando você está trabalhando em um pequeno grupo unido, não é incomum
+interagir com o mesmo repositório várias e várias vezes. Definindo um
+repositório remoto antes de tudo, você pode fazê-lo mais facilmente:
+
+------------------------------------------------
+alice$ git remote add bob /home/bob/myrepo
+------------------------------------------------
+
+Com isso, Alice pode executar a primeira parte da operação "pull" usando
+o comando 'git-fetch' sem unificar suas mudanças com seu próprio ramo,
+usando:
+
+-------------------------------------
+alice$ git fetch bob
+-------------------------------------
+
+Diferente da forma longa, quando Alice obteve de Bob usando um
+repositório remoto antes definido com 'git-remote', o que foi obtido é
+armazenado em um ramo remoto, neste caso `bob/master`. Então, após isso:
+
+-------------------------------------
+alice$ git log -p master..bob/master
+-------------------------------------
+
+mostra uma lista de todas as mudanças que Bob fez desde que ramificou do
+ramo master de Alice.
+
+Após examinar essas mudanças, Alice pode unificá-las em seu ramo master:
+
+-------------------------------------
+alice$ git merge bob/master
+-------------------------------------
+
+Esse `merge` pode também ser feito puxando de seu próprio ramo remoto,
+assim:
+
+-------------------------------------
+alice$ git pull . remotes/bob/master
+-------------------------------------
+
+Note que 'git pull' sempre unifica ao ramo atual, independente do que
+mais foi passado na linha de comando.
+
+Depois, Bob pode atualizar seu repositório com as últimas mudanças de
+Alice, usando
+
+-------------------------------------
+bob$ git pull
+-------------------------------------
+
+Note que ele não precisa dar o caminho do repositório de Alice; quando
+Bob clonou seu repositório, o git armazenou a localização de seu
+repositório na configuração do mesmo, e essa localização é usada
+para puxar:
+
+-------------------------------------
+bob$ git config --get remote.origin.url
+/home/alice/project
+-------------------------------------
+
+(A configuração completa criada por 'git-clone' é visível usando `git
+config -l`, e a página de manual linkgit:git-config[1] explica o
+significado de cada opção.)
+
+Git também mantém uma cópia limpa do ramo master de Alice sob o nome
+"origin/master":
+
+-------------------------------------
+bob$ git branch -r
+  origin/master
+-------------------------------------
+
+Se Bob decidir depois em trabalhar em um host diferente, ele ainda pode
+executar clones e puxar usando o protocolo ssh:
+
+-------------------------------------
+bob$ git clone alice.org:/home/alice/project myrepo
+-------------------------------------
+
+Alternativamente, o git tem um protocolo nativo, ou pode usar rsync ou
+http; veja linkgit:git-pull[1] para detalhes.
+
+Git pode também ser usado em um modo parecido com CVS, com um
+repositório central para o qual vários usuários empurram modificações;
+veja linkgit:git-push[1] e linkgit:gitcvs-migration[7].
+
+Explorando história
+-----------------
+
+A história no git é representada como uma série de commits
+interrelacionados. Nós já vimos que o comando 'git-log' pode listar
+esses commits. Note que a primeira linha de cada entrada no log também
+dá o nome para o commit:
+
+-------------------------------------
+$ git log
+commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
+Author: Junio C Hamano <junkio@cox.net>
+Date:   Tue May 16 17:18:22 2006 -0700
+
+    merge-base: Clarify the comments on post processing.
+-------------------------------------
+
+Nós podemos dar este nome ao 'git-show' para ver os detalhes sobre este
+commit.
+
+-------------------------------------
+$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
+-------------------------------------
+
+Mas há outras formas de se referir aos commits. Você pode usar qualquer
+parte inicial do nome que seja longo o bastante para identificar
+unicamente o commit:
+
+-------------------------------------
+$ git show c82a22c39c  # os primeiros caracteres do nome são o bastante
+                       # usualmente
+$ git show HEAD                # a ponta do ramo atual
+$ git show experimental        # a ponta do ramo "experimental"
+-------------------------------------
+
+Todo commit normalmente tem um commit "pai" que aponta para o estado
+anterior do projeto:
+
+-------------------------------------
+$ git show HEAD^  # para ver o pai de HEAD
+$ git show HEAD^^ # para ver o avô de HEAD
+$ git show HEAD~4 # para ver o trisavô de HEAD
+-------------------------------------
+
+Note que commits de unificação podem ter mais de um pai:
+
+-------------------------------------
+$ git show HEAD^1 # mostra o primeiro pai de HEAD (o mesmo que HEAD^)
+$ git show HEAD^2 # mostra o segundo pai de HEAD
+-------------------------------------
+
+Você também pode dar aos commits nomes à sua escolha; após executar
+
+-------------------------------------
+$ git tag v2.5 1b2e1d63ff
+-------------------------------------
+
+você pode se referir a 1b2e1d63ff pelo nome "v2.5". Se você pretende
+compartilhar esse nome com outras pessoas (por exemplo, para identificar
+uma versão de lançamento), você deveria criar um objeto "tag", e talvez
+assiná-lo; veja linkgit:git-tag[1] para detalhes.
+
+Qualquer comando git que precise conhecer um commit pode receber
+quaisquer desses nomes. Por exemplo:
+
+-------------------------------------
+$ git diff v2.5 HEAD    # compara o HEAD atual com v2.5
+$ git branch stable v2.5 # inicia um novo ramo chamado "stable" baseado
+                        # em v2.5
+$ git reset --hard HEAD^ # reseta seu ramo atual e seu diretório de
+                        # trabalho a seu estado em HEAD^
+-------------------------------------
+
+Seja cuidadoso com o último comando: além de perder quaisquer mudanças
+em seu diretório de trabalho, ele também remove todos os commits
+posteriores desse ramo. Se esse ramo é o único ramo contendo esses
+commits, eles serão perdidos. Também, não use 'git-reset' num ramo
+publicamente visível de onde outros desenvolvedores puxam, já que vai
+forçar unificações desnecessárias para que outros desenvolvedores limpem
+a história. Se você precisa desfazer mudanças que você empurrou, use
+'git-revert' no lugar.
+
+O comando 'git-grep' pode buscar strings em qualquer versão de seu
+projeto, então
+
+-------------------------------------
+$ git grep "hello" v2.5
+-------------------------------------
+
+procura por todas as ocorrências de "hello" em v2.5.
+
+Se você deixar de fora o nome do commit, 'git-grep' irá procurar
+quaisquer dos arquivos que ele gerencia no diretório corrente. Então
+
+-------------------------------------
+$ git grep "hello"
+-------------------------------------
+
+é uma forma rápida de buscar somente os arquivos que são rastreados pelo
+git.
+
+Muitos comandos git também recebem um conjunto de commits, o que pode
+ser especificado de várias formas. Aqui estão alguns exemplos com 'git-log':
+
+-------------------------------------
+$ git log v2.5..v2.6            # commits entre v2.5 e v2.6
+$ git log v2.5..                # commits desde v2.5
+$ git log --since="2 weeks ago" # commits das últimas 2 semanas
+$ git log v2.5.. Makefile       # commits desde v2.5 que modificam
+                               # Makefile
+-------------------------------------
+
+Você também pode dar ao 'git-log' um "intervalo" de commits onde o
+primeiro não é necessariamente um ancestral do segundo; por exemplo, se
+as pontas dos ramos "stable" e "master" divergiram de um commit
+comum algum tempo atrás, então
+
+-------------------------------------
+$ git log stable..master
+-------------------------------------
+
+irá listar os commits feitos no ramo "master" mas não no ramo
+"stable", enquanto
+
+-------------------------------------
+$ git log master..stable
+-------------------------------------
+
+irá listar a lista de commits feitos no ramo "stable" mas não no ramo
+"master".
+
+O comando 'git-log' tem uma fraqueza: ele precisa mostrar os commits em
+uma lista. Quando a história tem linhas de desenvolvimento que
+divergiram e então foram unificadas novamente, a ordem em que 'git-log'
+apresenta essas mudanças é irrelevante.
+
+A maioria dos projetos com múltiplos contribuidores (como o kernel
+Linux, ou o próprio git) tem unificações frequentes, e 'gitk' faz um
+trabalho melhor de visualizar sua história. Por exemplo,
+
+-------------------------------------
+$ gitk --since="2 weeks ago" drivers/
+-------------------------------------
+
+permite a você navegar em quaisquer commits desde as últimas duas semanas
+de commits que modificaram arquivos sob o diretório "drivers". (Nota:
+você pode ajustar as fontes do gitk segurando a tecla control enquanto
+pressiona "-" ou "+".)
+
+Finalmente, a maioria dos comandos que recebem nomes de arquivo permitirão
+também, opcionalmente, preceder qualquer nome de arquivo por um
+commit, para especificar uma versão particular do arquivo:
+
+-------------------------------------
+$ git diff v2.5:Makefile HEAD:Makefile.in
+-------------------------------------
+
+Você pode usar 'git-show' para ver tal arquivo:
+
+-------------------------------------
+$ git show v2.5:Makefile
+-------------------------------------
+
+Próximos passos
+----------
+
+Este tutorial deve ser o bastante para operar controle de revisão
+distribuído básico para seus projetos. No entanto, para entender
+plenamente a profundidade e o poder do git você precisa entender duas
+idéias simples nas quais ele se baseia:
+
+  * A base de objetos é um sistema bem elegante usado para armazenar a
+    história de seu projeto--arquivos, diretórios, e commits.
+
+  * O arquivo de índice é um cache do estado de uma árvore de diretório,
+    usado para criar commits, restaurar diretórios de trabalho, e
+    armazenar as várias árvores envolvidas em uma unificação.
+
+A parte dois deste tutorial explica a base de objetos, o arquivo de
+índice, e algumas outras coisinhas que você vai precisar pra usar o
+máximo do git. Você pode encontrá-la em linkgit:gittutorial-2[7].
+
+Se você não quiser continuar com o tutorial agora nesse momento, algumas
+outras digressões que podem ser interessantes neste ponto são:
+
+  * linkgit:git-format-patch[1], linkgit:git-am[1]: Estes convertem
+    séries de commits em patches para email, e vice-versa, úteis para
+    projetos como o kernel Linux que dependem fortemente de patches
+    enviados por email.
+
+  * linkgit:git-bisect[1]: Quando há uma regressão em seu projeto, uma
+    forma de rastrear um bug é procurando pela história para encontrar o
+    commit culpado. Git bisect pode ajudar a executar uma busca binária
+    por esse commit. Ele é inteligente o bastante para executar uma
+    busca próxima da ótima mesmo no caso de uma história complexa
+    não-linear com muitos ramos unificados.
+
+  * link:everyday.html[GIT diariamente com 20 e tantos comandos]
+
+  * linkgit:gitcvs-migration[7]: Git para usuários de CVS.
+
+VEJA TAMBÉM
+--------
+linkgit:gittutorial-2[7],
+linkgit:gitcvs-migration[7],
+linkgit:gitcore-tutorial[7],
+linkgit:gitglossary[7],
+linkgit:git-help[1],
+link:everyday.html[git diariamente],
+link:user-manual.html[O Manual do Usuário git]
+
+GIT
+---
+Parte da suite linkgit:git[1].
index 11eec941dff7302ffcd68eca3d6437d31c022ca9..bf66116d61af4f16cff59ed1563f38736a14ecce 100644 (file)
@@ -201,6 +201,10 @@ endif::git-rev-list[]
 
        Stop when a given path disappears from the tree.
 
+--merges::
+
+       Print only merge commits.
+
 --no-merges::
 
        Do not print commits with more than one parent.
index e66ca9f70c705841c864196a3a651e04d8156e01..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()`.
 
@@ -198,7 +215,7 @@ The function must be defined in this form:
 
 The callback mechanism is as follows:
 
-* Inside `funct`, the only interesting member of the structure
+* Inside `func`, the only interesting member of the structure
   given by `opt` is the void pointer `opt->value`.
   `\*opt->value` will be the value that is saved into `var`, if you
   use `OPT_CALLBACK()`.
index 073b22bd83badb5aada47e061bb29e48d5f95518..c54b17db69b8420bdec7c6ee0f424a5216957add 100644 (file)
@@ -18,6 +18,10 @@ struct remote
 
        An array of all of the url_nr URLs configured for the remote
 
+`pushurl`::
+
+       An array of all of the pushurl_nr push URLs configured for the remote
+
 `push`::
 
         An array of refspecs configured for pushing, with
index 2efe7a40be641bc2532c139637fe02e534ea1152..b26c28133c143b23acf28fc1a3a06d16c10c65f8 100644 (file)
@@ -35,12 +35,32 @@ Functions
        Convenience functions that encapsulate a sequence of
        start_command() followed by finish_command(). The argument argv
        specifies the program and its arguments. The argument opt is zero
-       or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or
-       `RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members
-       .no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`.
+       or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,
+       `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`
+       that correspond to the members .no_stdin, .git_cmd,
+       .stdout_to_stderr, .silent_exec_failure of `struct child_process`.
        The argument dir corresponds the member .dir. The argument env
        corresponds to the member .env.
 
+The functions above do the following:
+
+. If a system call failed, errno is set and -1 is returned. A diagnostic
+  is printed.
+
+. If the program was not found, then -1 is returned and errno is set to
+  ENOENT; a diagnostic is printed only if .silent_exec_failure is 0.
+
+. Otherwise, the program is run. If it terminates regularly, its exit
+  code is returned. No diagnistic is printed, even if the exit code is
+  non-zero.
+
+. If the program terminated due to a signal, then the return value is the
+  signal number - 128, ie. it is negative and so indicates an unusual
+  condition; a diagnostic is printed. This return value can be passed to
+  exit(2), which will report the same code to the parent process that a
+  POSIX shell's $? would report for a program that died from the signal.
+
+
 `start_async`::
 
        Run a function asynchronously. Takes a pointer to a `struct
@@ -143,6 +163,11 @@ string pointers (NULL terminated) in .env:
 To specify a new initial working directory for the sub-process,
 specify it in the .dir member.
 
+If the program cannot be found, the functions return -1 and set
+errno to ENOENT. Normally, an error message is printed, but if
+.silent_exec_failure is set to 1, no message is printed for this
+special error condition.
+
 
 * `struct async`
 
index e3ddf912841298d6317a682a29cbaf628e86f156..55b728632cb7fa18de0d1dc070f5007acef053e0 100644 (file)
 tree walking API
 ================
 
-Talk about <tree-walk.h>, things like
+The tree walking API is used to traverse and inspect trees.
 
-* struct tree_desc
-* init_tree_desc
-* tree_entry_extract
-* update_tree_entry
-* get_tree_entry
+Data Structures
+---------------
 
-(JC, Linus)
+`struct name_entry`::
+
+       An entry in a tree. Each entry has a sha1 identifier, pathname, and
+       mode.
+
+`struct tree_desc`::
+
+       A semi-opaque data structure used to maintain the current state of the
+       walk.
++
+* `buffer` is a pointer into the memory representation of the tree. It always
+points at the current entry being visited.
+
+* `size` counts the number of bytes left in the `buffer`.
+
+* `entry` points to the current entry being visited.
+
+`struct traverse_info`::
+
+       A structure used to maintain the state of a traversal.
++
+* `prev` points to the traverse_info which was used to descend into the
+current tree. If this is the top-level tree `prev` will point to
+a dummy traverse_info.
+
+* `name` is the entry for the current tree (if the tree is a subtree).
+
+* `pathlen` is the length of the full path for the current tree.
+
+* `conflicts` can be used by callbacks to maintain directory-file conflicts.
+
+* `fn` is a callback called for each entry in the tree. See Traversing for more
+information.
+
+* `data` can be anything the `fn` callback would want to use.
+
+Initializing
+------------
+
+`init_tree_desc`::
+
+       Initialize a `tree_desc` and decode its first entry. The buffer and
+       size parameters are assumed to be the same as the buffer and size
+       members of `struct tree`.
+
+`fill_tree_descriptor`::
+
+       Initialize a `tree_desc` and decode its first entry given the sha1 of
+       a tree. Returns the `buffer` member if the sha1 is a valid tree
+       identifier and NULL otherwise.
+
+`setup_traverse_info`::
+
+       Initialize a `traverse_info` given the pathname of the tree to start
+       traversing from. The `base` argument is assumed to be the `path`
+       member of the `name_entry` being recursed into unless the tree is a
+       top-level tree in which case the empty string ("") is used.
+
+Walking
+-------
+
+`tree_entry`::
+
+       Visit the next entry in a tree. Returns 1 when there are more entries
+       left to visit and 0 when all entries have been visited. This is
+       commonly used in the test of a while loop.
+
+`tree_entry_len`::
+
+       Calculate the length of a tree entry's pathname. This utilizes the
+       memory structure of a tree entry to avoid the overhead of using a
+       generic strlen().
+
+`update_tree_entry`::
+
+       Walk to the next entry in a tree. This is commonly used in conjunction
+       with `tree_entry_extract` to inspect the current entry.
+
+`tree_entry_extract`::
+
+       Decode the entry currently being visited (the one pointed to by
+       `tree_desc's` `entry` member) and return the sha1 of the entry. The
+       `pathp` and `modep` arguments are set to the entry's pathname and mode
+       respectively.
+
+`get_tree_entry`::
+
+       Find an entry in a tree given a pathname and the sha1 of a tree to
+       search. Returns 0 if the entry is found and -1 otherwise. The third
+       and fourth parameters are set to the entry's sha1 and mode
+       respectively.
+
+Traversing
+----------
+
+`traverse_trees`::
+
+       Traverse `n` number of trees in parallel. The `fn` callback member of
+       `traverse_info` is called once for each tree entry.
+
+`traverse_callback_t`::
+       The arguments passed to the traverse callback are as follows:
++
+* `n` counts the number of trees being traversed.
+
+* `mask` has its nth bit set if something exists in the nth entry.
+
+* `dirmask` has its nth bit set if the nth tree's entry is a directory.
+
+* `entry` is an array of size `n` where the nth entry is from the nth tree.
+
+* `info` maintains the state of the traversal.
+
++
+Returning a negative value will terminate the traversal. Otherwise the
+return value is treated as an update mask. If the nth bit is set the nth tree
+will be updated and if the bit is not set the nth tree entry will be the
+same in the next callback invocation.
+
+`make_traverse_path`::
+
+       Generate the full pathname of a tree entry based from the root of the
+       traversal. For example, if the traversal has recursed into another
+       tree named "bar" the pathname of an entry "baz" in the "bar"
+       tree would be "bar/baz".
+
+`traverse_path_len`::
+
+       Calculate the length of a pathname returned by `make_traverse_path`.
+       This utilizes the memory structure of a tree entry to avoid the
+       overhead of using a generic strlen().
+
+Authors
+-------
+
+Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds
+<torvalds@linux-foundation.org>
index 48bb97f0b11048f3773fe9fba234fe1160ca3906..53aa0c82c22c687db1e16041fe9f4a97b1fe064e 100644 (file)
@@ -42,10 +42,12 @@ compared, but this is not enabled by default because this member
 is not stable on network filesystems.  With `USE_NSEC`
 compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
 members are also compared, but this is not enabled by default
-because the value of this member becomes meaningless once the
-inode is evicted from the inode cache on filesystems that do not
-store it on disk.
-
+because in-core timestamps can have finer granularity than
+on-disk timestamps, resulting in meaningless changes when an
+inode is evicted from the inode cache.  See commit 8ce13b0
+of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
+([PATCH] Sync in core time granuality with filesystems,
+2005-01-04).
 
 Racy git
 --------
index 41ec7774f481fd2d70492be4ab2b5e0bf887fc0b..2a0e7b89441039699a3dc9514f1165cb2112020a 100644 (file)
@@ -27,10 +27,13 @@ config file would appear like this:
 ------------
        [remote "<name>"]
                url = <url>
+               pushurl = <pushurl>
                push = <refspec>
                fetch = <refspec>
 ------------
 
+The `<pushurl>` is used for pushes only. It is optional and defaults
+to `<url>`.
 
 Named file in `$GIT_DIR/remotes`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 5355ebc0f39114823f830e0651078a99f0ac2e70..d813ceb7239bc2d22eb0af4ad33a6e1be8408787 100644 (file)
@@ -67,3 +67,21 @@ For example, with this:
 a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
 rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
 
+If you want to rewrite URLs for push only, you can create a
+configuration section of the form:
+
+------------
+       [url "<actual url base>"]
+               pushInsteadOf = <other url base>
+------------
+
+For example, with this:
+
+------------
+       [url "ssh://example.org/"]
+               pushInsteadOf = git://example.org/
+------------
+
+a URL like "git://example.org/path/to/repo.git" will be rewritten to
+"ssh://example.org/path/to/repo.git" for pushes, but pulls will still
+use the original URL.
index dbbeb7e7c7856776450bc64321ff5f1de26202bb..67ebffa568fd684afd08496281a530c658a217d9 100644 (file)
@@ -1520,10 +1520,10 @@ $ git commit -a -m "blorpl: typofix"
 ------------------------------------------------
 
 After that, you can go back to what you were working on with
-`git stash apply`:
+`git stash pop`:
 
 ------------------------------------------------
-$ git stash apply
+$ git stash pop
 ------------------------------------------------
 
 
@@ -4131,7 +4131,7 @@ What does this mean?
 
 `git rev-list` is the original version of the revision walker, which
 _always_ printed a list of revisions to stdout.  It is still functional,
-and needs to, since most new Git programs start out as scripts using
+and needs to, since most new Git commands start out as scripts using
 `git rev-list`.
 
 `git rev-parse` is not as important any more; it was only used to filter out
index 39cde784c947fae6a2f294caac1898b9199d769d..710d361233e26a8cc2da94f87f8b436b5af3419b 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.3.GIT
+DEF_VER=v1.6.5.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index ae7f7508f8e8cffeb930c820e068ba70dabff7bd..be504c95e1f092edaf524064d49ba30adc57dc9b 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -13,6 +13,10 @@ that uses $prefix, the built results have some paths encoded,
 which are derived from $prefix, so "make all; make prefix=/usr
 install" would not work.
 
+The beginning of the Makefile documents many variables that affect the way
+git is built.  You can override them either from the command line, or in a
+config.mak file.
+
 Alternatively you can use autoconf generated ./configure script to
 set up install paths (via config.mak.autogen), so you can write instead
 
@@ -48,32 +52,42 @@ Issues of note:
        export GIT_EXEC_PATH PATH GITPERLLIB
 
  - Git is reasonably self-sufficient, but does depend on a few external
-   programs and libraries:
+   programs and libraries.  Git can be used without most of them by adding
+   the approriate "NO_<LIBRARY>=YesPlease" to the make command line or
+   config.mak file.
 
        - "zlib", the compression library. Git won't build without it.
 
-       - "openssl".  Unless you specify otherwise, you'll get the SHA1
-         library from here.
+       - "ssh" is used to push and pull over the net.
 
-         If you don't have openssl, you can use one of the SHA1 libraries
-         that come with git (git includes the one from Mozilla, and has
-         its own PowerPC and ARM optimized ones too - see the Makefile).
+       - A POSIX-compliant shell is required to run many scripts needed
+         for everyday use (e.g. "bisect", "pull").
 
-       - libcurl library; git-http-fetch and git-fetch use them.  You
-         might also want the "curl" executable for debugging purposes.
-         If you do not use http transfer, you are probably OK if you
-         do not have them.
+       - "Perl" is needed to use some of the features (e.g. preparing a
+         partial commit using "git add -i/-p", interacting with svn
+         repositories with "git svn").  If you can live without these, use
+         NO_PERL.
 
-       - expat library; git-http-push uses it for remote lock
-         management over DAV.  Similar to "curl" above, this is optional.
+       - "openssl" library is used by git-imap-send to use IMAP over SSL.
+         If you don't need it, use NO_OPENSSL.
 
-        - "wish", the Tcl/Tk windowing shell is used in gitk to show the
-          history graphically, and in git-gui.
+         By default, git uses OpenSSL for SHA1 but it will use it's own
+         library (inspired by Mozilla's) with either NO_OPENSSL or
+         BLK_SHA1.  Also included is a version optimized for PowerPC
+         (PPC_SHA1).
+
+       - "libcurl" library is used by git-http-fetch and git-fetch.  You
+         might also want the "curl" executable for debugging purposes.
+         If you do not use http:// or https:// repositories, you do not
+         have to have them (use NO_CURL).
 
-       - "ssh" is used to push and pull over the net
+       - "expat" library; git-http-push uses it for remote lock
+         management over DAV.  Similar to "curl" above, this is optional
+         (with NO_EXPAT).
 
-       - "perl" and POSIX-compliant shells are needed to use most of
-         the bare-bones Porcelainish scripts.
+       - "wish", the Tcl/Tk windowing shell is used in gitk to show the
+         history graphically, and in git-gui.  If you don't want gitk or
+         git-gui, you can use NO_TCLTK.
 
  - Some platform specific issues are dealt with Makefile rules,
    but depending on your specific installation, you may not
index 6e216436c36f4dcbe1121d02a291e9f568ca6071..42b7d60e195559c8c0a7c6b32ce3b7ab8cfacebd 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.
@@ -11,7 +16,7 @@ all::
 # when attempting to read from an fopen'ed directory.
 #
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
+# This also implies BLK_SHA1.
 #
 # Define NO_CURL if you do not have libcurl installed.  git-http-pull and
 # git-http-push are not built, and you cannot use http:// and https://
@@ -52,6 +57,12 @@ 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 NEEDS_LIBGEN if your libgen needs -lgen when linking
+#
 # 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.
@@ -73,24 +84,26 @@ all::
 # specify your own (or DarwinPort's) include directories and
 # library directories by defining CFLAGS and LDFLAGS appropriately.
 #
+# Define BLK_SHA1 environment variable if you want the C version
+# of the SHA1 that assumes you can do unaligned 32-bit loads and
+# have a fast htonl() function.
+#
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
+# Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
 #
-# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+# Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
 #
 # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
 #
 # 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.
@@ -140,7 +153,11 @@ all::
 #
 # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
 #
-# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
+# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72
+# (not v1.73 or v1.71).
+#
+# Define ASCIIDOC_NO_ROFF if your DocBook XSL escapes raw roff directives
+# (versions 1.72 and later and 1.68.1 and earlier).
 #
 # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
 # MakeMaker (e.g. using ActiveState under Cygwin).
@@ -175,6 +192,14 @@ all::
 # Define OBJECT_CREATION_USES_RENAMES if your operating systems has problems
 # when hardlinking a file to another name and unlinking the original file right
 # away (some NTFS drivers seem to zero the contents in that scenario).
+#
+# 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.
+#
+# Define NO_REGEX if you have no or inferior regex support in your C library.
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -225,6 +250,7 @@ ETC_GITCONFIG = etc/gitconfig
 endif
 lib = lib
 # DESTDIR=
+pathsep = :
 
 # default configuration for gitweb
 GITWEB_CONFIG = gitweb_config.perl
@@ -332,13 +358,11 @@ 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
 PROGRAMS += git-show-index$X
 PROGRAMS += git-unpack-file$X
-PROGRAMS += git-update-server-info$X
 PROGRAMS += git-upload-pack$X
 PROGRAMS += git-var$X
 
@@ -360,7 +384,8 @@ BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
 BUILT_INS += git-whatchanged$X
 
-# what 'all' will build and 'install' will install, in gitexecdir
+# what 'all' will build and 'install' will install in gitexecdir,
+# excluding programs for built-in commands
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 
 # what 'all' will build but not install in gitexecdir
@@ -379,6 +404,7 @@ export PERL_PATH
 LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 
+LIB_H += advice.h
 LIB_H += archive.h
 LIB_H += attr.h
 LIB_H += blob.h
@@ -436,6 +462,7 @@ LIB_H += utf8.h
 LIB_H += wt-status.h
 
 LIB_OBJS += abspath.o
+LIB_OBJS += advice.o
 LIB_OBJS += alias.o
 LIB_OBJS += alloc.o
 LIB_OBJS += archive.o
@@ -509,6 +536,7 @@ LIB_OBJS += read-cache.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += remote.o
+LIB_OBJS += replace_object.o
 LIB_OBJS += rerere.o
 LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
@@ -526,6 +554,7 @@ LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += trace.o
 LIB_OBJS += transport.o
+LIB_OBJS += transport-helper.o
 LIB_OBJS += tree-diff.o
 LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
@@ -586,6 +615,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
@@ -597,6 +627,7 @@ BUILTIN_OBJS += builtin-read-tree.o
 BUILTIN_OBJS += builtin-receive-pack.o
 BUILTIN_OBJS += builtin-reflog.o
 BUILTIN_OBJS += builtin-remote.o
+BUILTIN_OBJS += builtin-replace.o
 BUILTIN_OBJS += builtin-rerere.o
 BUILTIN_OBJS += builtin-reset.o
 BUILTIN_OBJS += builtin-rev-list.o
@@ -614,6 +645,7 @@ BUILTIN_OBJS += builtin-tar-tree.o
 BUILTIN_OBJS += builtin-unpack-objects.o
 BUILTIN_OBJS += builtin-update-index.o
 BUILTIN_OBJS += builtin-update-ref.o
+BUILTIN_OBJS += builtin-update-server-info.o
 BUILTIN_OBJS += builtin-upload-archive.o
 BUILTIN_OBJS += builtin-verify-pack.o
 BUILTIN_OBJS += builtin-verify-tag.o
@@ -632,10 +664,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)
@@ -647,6 +681,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
@@ -670,6 +705,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
@@ -678,6 +714,7 @@ ifeq ($(uname_S),SCO_SV)
        TAR = gtar
 endif
 ifeq ($(uname_S),Darwin)
+       NEEDS_CRYPTO_WITH_SSL = YesPlease
        NEEDS_SSL_WITH_CRYPTO = YesPlease
        NEEDS_LIBICONV = YesPlease
        ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
@@ -694,11 +731,24 @@ 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
+       NO_REGEX = YesPlease
+       NO_EXTERNAL_GREP = YesPlease
+       THREADED_DELTA_SEARCH = 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
@@ -711,15 +761,16 @@ ifeq ($(uname_S),SunOS)
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
-       INSTALL = ginstall
+       INSTALL = /usr/ucb/install
        TAR = gtar
-       BASIC_CFLAGS += -D__EXTENSIONS__
+       BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
 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
@@ -749,6 +800,7 @@ endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
        NO_MEMMEM = YesPlease
+       USE_ST_TIMESPEC = YesPlease
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
@@ -762,11 +814,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
@@ -782,24 +836,54 @@ endif
 ifeq ($(uname_S),GNU)
        # GNU/Hurd
        NO_STRLCPY=YesPlease
+       NO_MKSTEMPS = YesPlease
+endif
+ifeq ($(uname_S),IRIX)
+       NO_SETENV = YesPlease
+       NO_UNSETENV = YesPlease
+       NO_STRCASESTR = YesPlease
+       NO_MEMMEM = YesPlease
+       NO_MKSTEMPS = YesPlease
+       NO_MKDTEMP = YesPlease
+       # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads
+       # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set),
+       # git dies with a segmentation fault when trying to access the first
+       # entry of a reflog.  The conservative choice is made to always set
+       # NO_MMAP.  If you suspect that your compiler is not affected by this
+       # issue, comment out the NO_MMAP statement.
+       NO_MMAP = YesPlease
+       NO_EXTERNAL_GREP = UnfortunatelyYes
+       SNPRINTF_RETURNS_BOGUS = YesPlease
+       SHELL_PATH = /usr/gnu/bin/bash
+       NEEDS_LIBGEN = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),IRIX64)
-       NO_IPV6=YesPlease
        NO_SETENV=YesPlease
+       NO_UNSETENV = YesPlease
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
-       NO_STRLCPY = YesPlease
-       NO_SOCKADDR_STORAGE=YesPlease
+       NO_MKSTEMPS = YesPlease
+       NO_MKDTEMP = YesPlease
+       # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads
+       # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set),
+       # git dies with a segmentation fault when trying to access the first
+       # entry of a reflog.  The conservative choice is made to always set
+       # NO_MMAP.  If you suspect that your compiler is not affected by this
+       # issue, comment out the NO_MMAP statement.
+       NO_MMAP = YesPlease
+       NO_EXTERNAL_GREP = UnfortunatelyYes
+       SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
-       BASIC_CFLAGS += -DPATH_MAX=1024
-       # for now, build 32-bit version
-       BASIC_LDFLAGS += -L/usr/lib32
+       NEEDS_LIBGEN = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),HP-UX)
        NO_IPV6=YesPlease
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
+       NO_MKSTEMPS = YesPlease
        NO_STRLCPY = YesPlease
        NO_MKDTEMP = YesPlease
        NO_UNSETENV = YesPlease
@@ -811,10 +895,12 @@ ifneq (,$(findstring CYGWIN,$(uname_S)))
        COMPAT_OBJS += compat/cygwin.o
        UNRELIABLE_FSTAT = UnfortunatelyYes
 endif
-ifneq (,$(findstring MINGW,$(uname_S)))
+ifdef MSVC
+       GIT_VERSION := $(GIT_VERSION).MSVC
+       pathsep = ;
        NO_PREAD = YesPlease
        NO_OPENSSL = YesPlease
-       NO_CURL = YesPlease
+       NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NO_IPV6 = YesPlease
        NO_SETENV = YesPlease
@@ -822,12 +908,64 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
        NO_MEMMEM = YesPlease
+       # NEEDS_LIBICONV = YesPlease
+       NO_ICONV = YesPlease
+       NO_C99_FORMAT = YesPlease
+       NO_STRTOUMAX = YesPlease
+       NO_STRTOULL = YesPlease
+       NO_MKDTEMP = YesPlease
+       NO_MKSTEMPS = YesPlease
+       SNPRINTF_RETURNS_BOGUS = YesPlease
+       NO_SVN_TESTS = YesPlease
+       NO_PERL_MAKEMAKER = YesPlease
+       RUNTIME_PREFIX = YesPlease
+       NO_POSIX_ONLY_PROGRAMS = YesPlease
+       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
+       NO_REGEX = YesPlease
+       NO_CURL = YesPlease
        NO_PTHREADS = YesPlease
+
+       CC = compat/vcbuild/scripts/clink.pl
+       AR = compat/vcbuild/scripts/lib.pl
+       CFLAGS =
+       BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
+       COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o
+       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -DSTRIP_EXTENSION=\".exe\"
+       BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
+       EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
+       lib =
+ifndef DEBUG
+       BASIC_CFLAGS += -GL -Os -MT
+       BASIC_LDFLAGS += -LTCG
+       AR += -LTCG
+else
+       BASIC_CFLAGS += -Zi -MTd
+endif
+       X = .exe
+else
+ifneq (,$(findstring MINGW,$(uname_S)))
+       pathsep = ;
+       NO_PREAD = YesPlease
+       NO_OPENSSL = YesPlease
+       NO_LIBGEN_H = YesPlease
+       NO_SYMLINK_HEAD = YesPlease
+       NO_IPV6 = YesPlease
+       NO_SETENV = YesPlease
+       NO_UNSETENV = YesPlease
+       NO_STRCASESTR = YesPlease
+       NO_STRLCPY = YesPlease
+       NO_MEMMEM = 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
@@ -836,22 +974,41 @@ 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
+       NO_REGEX = YesPlease
+       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
-       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o
+       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.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
 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)
@@ -878,6 +1035,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
@@ -888,9 +1050,7 @@ else
        else
                CURL_LIBCURL = -lcurl
        endif
-       BUILTIN_OBJS += builtin-http-fetch.o
-       EXTLIBS += $(CURL_LIBCURL)
-       LIB_OBJS += http.o http-walker.o
+       PROGRAMS += git-remote-curl$X git-http-fetch$X
        curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
@@ -925,9 +1085,12 @@ ifndef NO_OPENSSL
        else
                OPENSSL_LINK =
        endif
+       ifdef NEEDS_CRYPTO_WITH_SSL
+               OPENSSL_LINK += -lcrypto
+       endif
 else
        BASIC_CFLAGS += -DNO_OPENSSL
-       MOZILLA_SHA1 = 1
+       BLK_SHA1 = 1
        OPENSSL_LIBSSL =
 endif
 ifdef NEEDS_SSL_WITH_CRYPTO
@@ -944,12 +1107,18 @@ ifdef NEEDS_LIBICONV
        endif
        EXTLIBS += $(ICONV_LINK) -liconv
 endif
+ifdef NEEDS_LIBGEN
+       EXTLIBS += -lgen
+endif
 ifdef NEEDS_SOCKET
        EXTLIBS += -lsocket
 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
@@ -1005,6 +1174,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
@@ -1066,23 +1239,18 @@ ifdef NO_DEFLATE_BOUND
        BASIC_CFLAGS += -DNO_DEFLATE_BOUND
 endif
 
+ifdef BLK_SHA1
+       SHA1_HEADER = "block-sha1/sha1.h"
+       LIB_OBJS += block-sha1/sha1.o
+else
 ifdef PPC_SHA1
        SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
-else
-ifdef ARM_SHA1
-       SHA1_HEADER = "arm/sha1.h"
-       LIB_OBJS += arm/sha1.o arm/sha1_arm.o
-else
-ifdef MOZILLA_SHA1
-       SHA1_HEADER = "mozilla-sha1/sha1.h"
-       LIB_OBJS += mozilla-sha1/sha1.o
 else
        SHA1_HEADER = <openssl/sha.h>
        EXTLIBS += $(LIB_4_CRYPTO)
 endif
 endif
-endif
 ifdef NO_PERL_MAKEMAKER
        export NO_PERL_MAKEMAKER
 endif
@@ -1122,6 +1290,15 @@ endif
 ifdef UNRELIABLE_FSTAT
        BASIC_CFLAGS += -DUNRELIABLE_FSTAT
 endif
+ifdef NO_REGEX
+       COMPAT_CFLAGS += -Icompat/regex
+       COMPAT_OBJS += compat/regex/regex.o
+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
@@ -1147,6 +1324,7 @@ ifndef V
        QUIET_LINK     = @echo '   ' LINK $@;
        QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
        QUIET_GEN      = @echo '   ' GEN $@;
+       QUIET_LNCP     = @echo '   ' LN/CP $@;
        QUIET_SUBDIR0  = +@subdir=
        QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
                         $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1197,7 +1375,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::
@@ -1221,7 +1399,7 @@ strip: $(PROGRAMS) git$X
 git.o: git.c common-cmds.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
                '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
-               $(ALL_CFLAGS) -c $(filter %.c,$^)
+               $(ALL_CFLAGS) -o $@ -c $(filter %.c,$^)
 
 git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
@@ -1248,9 +1426,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 $@+ $@
@@ -1267,7 +1445,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 '}' \
@@ -1374,14 +1552,23 @@ git-imap-send$X: imap-send.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
 
-http.o http-walker.o http-push.o transport.o: http.h
+http.o http-walker.o http-push.o: http.h
 
+http.o http-walker.o: $(LIB_H)
+
+git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o $(GITLIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+               $(LIBS) $(CURL_LIBCURL)
 git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
+git-remote-curl$X: remote-curl.o http.o http-walker.o $(GITLIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+               $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+
 $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
-$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
+$(patsubst git-%$X,%.o,$(PROGRAMS)) git.o: $(LIB_H) $(wildcard */*.h)
 builtin-revert.o wt-status.o: wt-status.h
 
 $(LIB_FILE): $(LIB_OBJS)
@@ -1490,6 +1677,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)
@@ -1548,14 +1737,16 @@ ifneq (,$X)
 endif
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
-       { $(RM) "$$execdir/git-add$X" && \
-               ln "$$bindir/git$X" "$$execdir/git-add$X" 2>/dev/null || \
-               cp "$$bindir/git$X" "$$execdir/git-add$X"; } && \
-       { for p in $(filter-out git-add$X,$(BUILT_INS)); do \
+       { test "$$bindir/" = "$$execdir/" || \
+               { $(RM) "$$execdir/git$X" && \
+               test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
+               ln "$$bindir/git$X" "$$execdir/git$X" 2>/dev/null || \
+               cp "$$bindir/git$X" "$$execdir/git$X"; } ; } && \
+       { for p in $(BUILT_INS); do \
                $(RM) "$$execdir/$$p" && \
-               ln "$$execdir/git-add$X" "$$execdir/$$p" 2>/dev/null || \
-               ln -s "git-add$X" "$$execdir/$$p" 2>/dev/null || \
-               cp "$$execdir/git-add$X" "$$execdir/$$p" || exit; \
+               ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
+               ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
+               cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
          done; } && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
@@ -1636,7 +1827,7 @@ distclean: clean
        $(RM) configure
 
 clean:
-       $(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
+       $(RM) *.o block-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)
diff --git a/README b/README
index c932ab310510c84c3e69107b458a228db37cb1f6..67cfeb2016b24df1cb406c18145efd399f6a1792 100644 (file)
--- a/README
+++ b/README
@@ -37,7 +37,7 @@ CVS users may also want to read Documentation/gitcvs-migration.txt
 ("man gitcvs-migration" or "git help cvs-migration" if git is
 installed).
 
-Many Git online resources are accessible from http://git.or.cz/
+Many Git online resources are accessible from http://git-scm.com/
 including full documentation and Git related tools.
 
 The user discussion and development of Git take place on the Git
index dd8bc4bb4ab75e1148cb9b7aaf8ff7384ebb19ec..5274ceed4e7ecd0210ad8338a0b3570e3648b05e 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.3.txt
\ No newline at end of file
+Documentation/RelNotes-1.6.6.txt
\ No newline at end of file
index 649f34f83365db3513c5166b897c4f033831444d..b88122cbe73ec0c438e2d375fdebd51e5febf9ae 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -18,7 +18,7 @@ const char *make_absolute_path(const char *path)
 {
        static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
        char cwd[1024] = "";
-       int buf_index = 1, len;
+       int buf_index = 1;
 
        int depth = MAXDEPTH;
        char *last_elem = NULL;
@@ -41,16 +41,16 @@ const char *make_absolute_path(const char *path)
 
                if (*buf) {
                        if (!*cwd && !getcwd(cwd, sizeof(cwd)))
-                               die ("Could not get current working directory");
+                               die_errno ("Could not get current working directory");
 
                        if (chdir(buf))
-                               die ("Could not switch to '%s'", buf);
+                               die_errno ("Could not switch to '%s'", buf);
                }
                if (!getcwd(buf, PATH_MAX))
-                       die ("Could not get current working directory");
+                       die_errno ("Could not get current working directory");
 
                if (last_elem) {
-                       int len = strlen(buf);
+                       size_t len = strlen(buf);
                        if (len + strlen(last_elem) + 2 > PATH_MAX)
                                die ("Too long path name: '%s/%s'",
                                                buf, last_elem);
@@ -61,9 +61,9 @@ const char *make_absolute_path(const char *path)
                }
 
                if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
-                       len = readlink(buf, next_buf, PATH_MAX);
+                       ssize_t len = readlink(buf, next_buf, PATH_MAX);
                        if (len < 0)
-                               die ("Invalid symlink: %s", buf);
+                               die_errno ("Invalid symlink '%s'", buf);
                        if (PATH_MAX <= len)
                                die("symbolic link too long: %s", buf);
                        next_buf[len] = '\0';
@@ -75,7 +75,7 @@ const char *make_absolute_path(const char *path)
        }
 
        if (*cwd && chdir(cwd))
-               die ("Could not change back to '%s'", cwd);
+               die_errno ("Could not change back to '%s'", cwd);
 
        return buf;
 }
@@ -109,7 +109,7 @@ const char *make_nonrelative_path(const char *path)
        } else {
                const char *cwd = get_pwd_cwd();
                if (!cwd)
-                       die("Cannot determine the current working directory");
+                       die_errno("Cannot determine the current working directory");
                if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
                        die("Too long path: %.*s", 60, path);
        }
diff --git a/advice.c b/advice.c
new file mode 100644 (file)
index 0000000..ae4b1e8
--- /dev/null
+++ b/advice.c
@@ -0,0 +1,27 @@
+#include "cache.h"
+
+int advice_push_nonfastforward = 1;
+int advice_status_hints = 1;
+
+static struct {
+       const char *name;
+       int *preference;
+} advice_config[] = {
+       { "pushnonfastforward", &advice_push_nonfastforward },
+       { "statushints", &advice_status_hints },
+};
+
+int git_default_advice_config(const char *var, const char *value)
+{
+       const char *k = skip_prefix(var, "advice.");
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(advice_config); i++) {
+               if (strcmp(k, advice_config[i].name))
+                       continue;
+               *advice_config[i].preference = git_config_bool(var, value);
+               return 0;
+       }
+
+       return 0;
+}
diff --git a/advice.h b/advice.h
new file mode 100644 (file)
index 0000000..e9df8e0
--- /dev/null
+++ b/advice.h
@@ -0,0 +1,9 @@
+#ifndef ADVICE_H
+#define ADVICE_H
+
+extern int advice_push_nonfastforward;
+extern int advice_status_hints;
+
+int git_default_advice_config(const char *var, const char *value);
+
+#endif /* ADVICE_H */
diff --git a/alias.c b/alias.c
index e687fe54c1ce2a435e8f0d7806db231c06f998eb..372b7d809301f9e3e936459e405cd6f2627bd4a9 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -38,10 +38,7 @@ int split_cmdline(char *cmdline, const char ***argv)
                        while (cmdline[++src]
                                        && isspace(cmdline[src]))
                                ; /* skip */
-                       if (count >= size) {
-                               size += 16;
-                               *argv = xrealloc(*argv, sizeof(char *) * size);
-                       }
+                       ALLOC_GROW(*argv, count+1, size);
                        (*argv)[count++] = cmdline + dst;
                } else if (!quoted && (c == '\'' || c == '"')) {
                        quoted = c;
@@ -72,6 +69,9 @@ int split_cmdline(char *cmdline, const char ***argv)
                return error("unclosed quote");
        }
 
+       ALLOC_GROW(*argv, count+1, size);
+       (*argv)[count] = NULL;
+
        return count;
 }
 
index ba890ebdecd8672aeb32757605c8a2976fa21161..cee06ce3cbc1819008337633ed0753638c423ac5 100644 (file)
@@ -180,7 +180,7 @@ static int write_tar_entry(struct archiver_args *args,
 
        sprintf(header.mode, "%07o", mode & 07777);
        sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
-       sprintf(header.mtime, "%011lo", args->time);
+       sprintf(header.mtime, "%011lo", (unsigned long) args->time);
 
        sprintf(header.uid, "%07o", 0);
        sprintf(header.gid, "%07o", 0);
index b2b90d31700d3aa50a3ac0626c1a8561c37c49b7..0cc79d2a240ac260fec66e6c30e28b438bcb5948 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -115,6 +115,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
 
        strbuf_reset(&path);
        strbuf_grow(&path, PATH_MAX);
+       strbuf_add(&path, args->base, args->baselen);
        strbuf_add(&path, base, baselen);
        strbuf_addstr(&path, filename);
        path_without_prefix = path.buf + args->baselen;
@@ -187,8 +188,8 @@ int write_archive_entries(struct archiver_args *args,
                git_attr_set_direction(GIT_ATTR_INDEX, &the_index);
        }
 
-       err =  read_tree_recursive(args->tree, args->base, args->baselen, 0,
-                       args->pathspec, write_archive_entry, &context);
+       err = read_tree_recursive(args->tree, "", 0, 0, args->pathspec,
+                                 write_archive_entry, &context);
        if (err == READ_TREE_RECURSIVE)
                err = 0;
        return err;
@@ -211,7 +212,7 @@ static const struct archiver *lookup_archiver(const char *name)
 static void parse_pathspec_arg(const char **pathspec,
                struct archiver_args *ar_args)
 {
-       ar_args->pathspec = get_pathspec(ar_args->base, pathspec);
+       ar_args->pathspec = get_pathspec("", pathspec);
 }
 
 static void parse_treeish_arg(const char **argv,
@@ -283,7 +284,7 @@ static int parse_archive_args(int argc, const char **argv,
                OPT_STRING(0, "format", &format, "fmt", "archive format"),
                OPT_STRING(0, "prefix", &base, "prefix",
                        "prepend prefix to each pathname in the archive"),
-               OPT_STRING(0, "output", &output, "file",
+               OPT_STRING('o', "output", &output, "file",
                        "write the archive to this file"),
                OPT_BOOLEAN(0, "worktree-attributes", &worktree_attributes,
                        "read .gitattributes in working directory"),
@@ -309,7 +310,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/arm/sha1.c b/arm/sha1.c
deleted file mode 100644 (file)
index c61ad4a..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SHA-1 implementation optimized for ARM
- *
- * Copyright:   (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created:     September 17, 2005
- */
-
-#include <string.h>
-#include "sha1.h"
-
-extern void arm_sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
-
-void arm_SHA1_Init(arm_SHA_CTX *c)
-{
-       c->len = 0;
-       c->hash[0] = 0x67452301;
-       c->hash[1] = 0xefcdab89;
-       c->hash[2] = 0x98badcfe;
-       c->hash[3] = 0x10325476;
-       c->hash[4] = 0xc3d2e1f0;
-}
-
-void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n)
-{
-       uint32_t workspace[80];
-       unsigned int partial;
-       unsigned long done;
-
-       partial = c->len & 0x3f;
-       c->len += n;
-       if ((partial + n) >= 64) {
-               if (partial) {
-                       done = 64 - partial;
-                       memcpy(c->buffer + partial, p, done);
-                       arm_sha_transform(c->hash, c->buffer, workspace);
-                       partial = 0;
-               } else
-                       done = 0;
-               while (n >= done + 64) {
-                       arm_sha_transform(c->hash, p + done, workspace);
-                       done += 64;
-               }
-       } else
-               done = 0;
-       if (n - done)
-               memcpy(c->buffer + partial, p + done, n - done);
-}
-
-void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c)
-{
-       uint64_t bitlen;
-       uint32_t bitlen_hi, bitlen_lo;
-       unsigned int i, offset, padlen;
-       unsigned char bits[8];
-       static const unsigned char padding[64] = { 0x80, };
-
-       bitlen = c->len << 3;
-       offset = c->len & 0x3f;
-       padlen = ((offset < 56) ? 56 : (64 + 56)) - offset;
-       arm_SHA1_Update(c, padding, padlen);
-
-       bitlen_hi = bitlen >> 32;
-       bitlen_lo = bitlen & 0xffffffff;
-       bits[0] = bitlen_hi >> 24;
-       bits[1] = bitlen_hi >> 16;
-       bits[2] = bitlen_hi >> 8;
-       bits[3] = bitlen_hi;
-       bits[4] = bitlen_lo >> 24;
-       bits[5] = bitlen_lo >> 16;
-       bits[6] = bitlen_lo >> 8;
-       bits[7] = bitlen_lo;
-       arm_SHA1_Update(c, bits, 8);
-
-       for (i = 0; i < 5; i++) {
-               uint32_t v = c->hash[i];
-               hash[0] = v >> 24;
-               hash[1] = v >> 16;
-               hash[2] = v >> 8;
-               hash[3] = v;
-               hash += 4;
-       }
-}
diff --git a/arm/sha1.h b/arm/sha1.h
deleted file mode 100644 (file)
index b61b618..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SHA-1 implementation optimized for ARM
- *
- * Copyright:  (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created:    September 17, 2005
- */
-
-#include <stdint.h>
-
-typedef struct {
-       uint64_t len;
-       uint32_t hash[5];
-       unsigned char buffer[64];
-} arm_SHA_CTX;
-
-void arm_SHA1_Init(arm_SHA_CTX *c);
-void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n);
-void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c);
-
-#define git_SHA_CTX    arm_SHA_CTX
-#define git_SHA1_Init  arm_SHA1_Init
-#define git_SHA1_Update        arm_SHA1_Update
-#define git_SHA1_Final arm_SHA1_Final
diff --git a/arm/sha1_arm.S b/arm/sha1_arm.S
deleted file mode 100644 (file)
index 41e9263..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- *  SHA transform optimized for ARM
- *
- *  Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- *  Created:   September 17, 2005
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- */
-
-       .text
-       .globl  arm_sha_transform
-
-/*
- * void sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
- *
- * note: the "data" pointer may be unaligned.
- */
-
-arm_sha_transform:
-
-       stmfd   sp!, {r4 - r8, lr}
-
-       @ for (i = 0; i < 16; i++)
-       @         W[i] = ntohl(((uint32_t *)data)[i]);
-
-#ifdef __ARMEB__
-       mov     r4, r0
-       mov     r0, r2
-       mov     r2, #64
-       bl      memcpy
-       mov     r2, r0
-       mov     r0, r4
-#else
-       mov     r3, r2
-       mov     lr, #16
-1:     ldrb    r4, [r1], #1
-       ldrb    r5, [r1], #1
-       ldrb    r6, [r1], #1
-       ldrb    r7, [r1], #1
-       subs    lr, lr, #1
-       orr     r5, r5, r4, lsl #8
-       orr     r6, r6, r5, lsl #8
-       orr     r7, r7, r6, lsl #8
-       str     r7, [r3], #4
-       bne     1b
-#endif
-
-       @ for (i = 0; i < 64; i++)
-       @         W[i+16] = ror(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 31);
-
-       sub     r3, r2, #4
-       mov     lr, #64
-2:     ldr     r4, [r3, #4]!
-       subs    lr, lr, #1
-       ldr     r5, [r3, #8]
-       ldr     r6, [r3, #32]
-       ldr     r7, [r3, #52]
-       eor     r4, r4, r5
-       eor     r4, r4, r6
-       eor     r4, r4, r7
-       mov     r4, r4, ror #31
-       str     r4, [r3, #64]
-       bne     2b
-
-       /*
-        * The SHA functions are:
-        *
-        * f1(B,C,D) = (D ^ (B & (C ^ D)))
-        * f2(B,C,D) = (B ^ C ^ D)
-        * f3(B,C,D) = ((B & C) | (D & (B | C)))
-        *
-        * Then the sub-blocks are processed as follows:
-        *
-        * A' = ror(A, 27) + f(B,C,D) + E + K + *W++
-        * B' = A
-        * C' = ror(B, 2)
-        * D' = C
-        * E' = D
-        *
-        * We therefore unroll each loop 5 times to avoid register shuffling.
-        * Also the ror for C (and also D and E which are successivelyderived
-        * from it) is applied in place to cut on an additional mov insn for
-        * each round.
-        */
-
-       .macro  sha_f1, A, B, C, D, E
-       ldr     r3, [r2], #4
-       eor     ip, \C, \D
-       add     \E, r1, \E, ror #2
-       and     ip, \B, ip, ror #2
-       add     \E, \E, \A, ror #27
-       eor     ip, ip, \D, ror #2
-       add     \E, \E, r3
-       add     \E, \E, ip
-       .endm
-
-       .macro  sha_f2, A, B, C, D, E
-       ldr     r3, [r2], #4
-       add     \E, r1, \E, ror #2
-       eor     ip, \B, \C, ror #2
-       add     \E, \E, \A, ror #27
-       eor     ip, ip, \D, ror #2
-       add     \E, \E, r3
-       add     \E, \E, ip
-       .endm
-
-       .macro  sha_f3, A, B, C, D, E
-       ldr     r3, [r2], #4
-       add     \E, r1, \E, ror #2
-       orr     ip, \B, \C, ror #2
-       add     \E, \E, \A, ror #27
-       and     ip, ip, \D, ror #2
-       add     \E, \E, r3
-       and     r3, \B, \C, ror #2
-       orr     ip, ip, r3
-       add     \E, \E, ip
-       .endm
-
-       ldmia   r0, {r4 - r8}
-
-       mov     lr, #4
-       ldr     r1, .L_sha_K + 0
-
-       /* adjust initial values */
-       mov     r6, r6, ror #30
-       mov     r7, r7, ror #30
-       mov     r8, r8, ror #30
-
-3:     subs    lr, lr, #1
-       sha_f1  r4, r5, r6, r7, r8
-       sha_f1  r8, r4, r5, r6, r7
-       sha_f1  r7, r8, r4, r5, r6
-       sha_f1  r6, r7, r8, r4, r5
-       sha_f1  r5, r6, r7, r8, r4
-       bne     3b
-
-       ldr     r1, .L_sha_K + 4
-       mov     lr, #4
-
-4:     subs    lr, lr, #1
-       sha_f2  r4, r5, r6, r7, r8
-       sha_f2  r8, r4, r5, r6, r7
-       sha_f2  r7, r8, r4, r5, r6
-       sha_f2  r6, r7, r8, r4, r5
-       sha_f2  r5, r6, r7, r8, r4
-       bne     4b
-
-       ldr     r1, .L_sha_K + 8
-       mov     lr, #4
-
-5:     subs    lr, lr, #1
-       sha_f3  r4, r5, r6, r7, r8
-       sha_f3  r8, r4, r5, r6, r7
-       sha_f3  r7, r8, r4, r5, r6
-       sha_f3  r6, r7, r8, r4, r5
-       sha_f3  r5, r6, r7, r8, r4
-       bne     5b
-
-       ldr     r1, .L_sha_K + 12
-       mov     lr, #4
-
-6:     subs    lr, lr, #1
-       sha_f2  r4, r5, r6, r7, r8
-       sha_f2  r8, r4, r5, r6, r7
-       sha_f2  r7, r8, r4, r5, r6
-       sha_f2  r6, r7, r8, r4, r5
-       sha_f2  r5, r6, r7, r8, r4
-       bne     6b
-
-       ldmia   r0, {r1, r2, r3, ip, lr}
-       add     r4, r1, r4
-       add     r5, r2, r5
-       add     r6, r3, r6, ror #2
-       add     r7, ip, r7, ror #2
-       add     r8, lr, r8, ror #2
-       stmia   r0, {r4 - r8}
-
-       ldmfd   sp!, {r4 - r8, pc}
-
-.L_sha_K:
-       .word   0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
diff --git a/attr.c b/attr.c
index 98eb636f13d314c20e18e646c074e2511b3c891c..55bdb7cdebea7f7ea551231fe7801f272d128d69 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++;
@@ -556,6 +555,8 @@ static void prepare_attr_stack(const char *path, int dirlen)
                }
        }
 
+       strbuf_release(&pathbuf);
+
        /*
         * Finally push the "info" one at the top of the stack.
         */
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..dc18db8af96b093d7cb16eb88d3f738f98dc30f6 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,14 +454,14 @@ static int read_bisect_refs(void)
        return for_each_ref_in("refs/bisect/", register_ref, NULL);
 }
 
-void read_bisect_paths(void)
+static void read_bisect_paths(struct argv_array *array)
 {
        struct strbuf str = STRBUF_INIT;
        const char *filename = git_path("BISECT_NAMES");
        FILE *fp = fopen(filename, "r");
 
        if (!fp)
-               die("Could not open file '%s': %s", filename, strerror(errno));
+               die_errno("Could not open file '%s'", filename);
 
        while (strbuf_getline(&str, fp, '\n') != EOF) {
                char *quoted;
@@ -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,527 @@ 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(array->sha1, array->sha1_nr, sizeof(*array->sha1), array_cmp);
+
+       array->sorted = 1;
+}
+
+static const unsigned char *sha1_access(size_t index, void *table)
 {
-       qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp);
+       unsigned char (*array)[20] = table;
+       return array[index];
 }
 
-static const unsigned char *skipped_sha1_access(size_t index, void *table)
+static int lookup_sha1_array(struct sha1_array *array,
+                            const unsigned char *sha1)
 {
-       unsigned char (*skipped)[20] = table;
-       return skipped[index];
+       if (!array->sorted)
+               sort_sha1_array(array);
+
+       return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access);
 }
 
-static int lookup_skipped(unsigned char *sha1)
+static char *join_sha1_array_hex(struct sha1_array *array, char delim)
 {
-       return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr,
-                       skipped_sha1_access);
+       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)
+#define PRN_MODULO 32768
+
+/*
+ * This is a pseudo random number generator based on "man 3 rand".
+ * It is not used properly because the seed is the argument and it
+ * is increased by one between each call, but that should not matter
+ * for this application.
+ */
+int get_prn(int count) {
+       count = count * 1103515245 + 12345;
+       return ((unsigned)(count/65536) % PRN_MODULO);
+}
+
+/*
+ * Custom integer square root from
+ * http://en.wikipedia.org/wiki/Integer_square_root
+ */
+static int sqrti(int val)
+{
+       float d, x = val;
+
+       if (val == 0)
+               return 0;
+
+       do {
+               float y = (x + (float)val / x) / 2;
+               d = (y > x) ? y - x : x - y;
+               x = y;
+       } while (d >= 0.5);
+
+       return (int)x;
+}
+
+static struct commit_list *skip_away(struct commit_list *list, int count)
+{
+       struct commit_list *cur, *previous;
+       int prn, index, i;
+
+       prn = get_prn(count);
+       index = (count * prn / PRN_MODULO) * sqrti(prn) / sqrti(PRN_MODULO);
+
+       cur = list;
+       previous = NULL;
+
+       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;
+
+       *tried = NULL;
+
+       if (!skipped_revs.sha1_nr)
+               return list;
+
+       list = filter_skipped(list, tried, 0, &count, &skipped_first);
+
+       if (!skipped_first)
+               return list;
+
+       return skip_away(list, count);
+}
+
+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);
+}
+
+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;
+
+       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++] = xstrdup("--");
+       strbuf_release(&str);
+       fclose(fp);
+
+       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);
+
+       if (fd < 0)
+               die_errno("could not create file '%s'", filename);
+
+       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));
+}
+
+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);
+}
+
+static 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);
 
-       read_bisect_paths();
+       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 object_array pending_copy;
+       int i, res;
+
+       bisect_rev_setup(&revs, prefix, "^%s", "%s", 0);
+
+       /* 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);
+
+       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;
 
-       ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
-       rev_argv[rev_argv_nr++] = NULL;
+       /* This is what "--pretty" does */
+       opt.verbose_header = 1;
+       opt.use_terminator = 0;
+       opt.commit_format = CMIT_FMT_DEFAULT;
 
-       setup_revisions(rev_argv_nr, rev_argv, revs, NULL);
+       /* diff-tree init */
+       if (!opt.diffopt.output_format)
+               opt.diffopt.output_format = DIFF_FORMAT_RAW;
 
-       revs->limited = 1;
+       log_tree_commit(&opt, commit);
 }
 
-int bisect_next_vars(const char *prefix)
+/*
+ * 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 rev_list_info info;
-       int reaches = 0, all = 0;
+       struct commit_list *tried;
+       int reaches = 0, all = 0, nr;
+       const unsigned char *bisect_rev;
+       char bisect_rev_hex[41];
 
-       memset(&info, 0, sizeof(info));
-       info.revs = &revs;
-       info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED;
+       if (read_bisect_refs())
+               die("reading bisect refs failed");
 
-       bisect_rev_setup(&revs, prefix);
+       check_good_are_ancestors_of_bad(prefix);
 
-       if (prepare_revision_walk(&revs))
-               die("revision walk setup failed");
-       if (revs.tree_objects)
-               mark_edges_uninteresting(revs.commits, &revs, NULL);
+       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);
+       }
 
-       return show_bisect_vars(&info, reaches, all);
+       bisect_rev = revs.commits->item->object.sha1;
+       memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41);
+
+       if (!hashcmp(bisect_rev, current_bad_sha1)) {
+               exit_if_skipped_commits(tried, current_bad_sha1);
+               printf("%s is the 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
diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c
new file mode 100644 (file)
index 0000000..d893475
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+/* this is only to get definitions for memcpy(), ntohl() and htonl() */
+#include "../git-compat-util.h"
+
+#include "sha1.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+/*
+ * Force usage of rol or ror by selecting the one with the smaller constant.
+ * It _can_ generate slightly smaller code (a constant of 1 is special), but
+ * perhaps more importantly it's possibly faster on any uarch that does a
+ * rotate with a loop.
+ */
+
+#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
+#define SHA_ROL(x,n)   SHA_ASM("rol", x, n)
+#define SHA_ROR(x,n)   SHA_ASM("ror", x, n)
+
+#else
+
+#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
+#define SHA_ROL(X,n)   SHA_ROT(X,n,32-(n))
+#define SHA_ROR(X,n)   SHA_ROT(X,32-(n),n)
+
+#endif
+
+/*
+ * If you have 32 registers or more, the compiler can (and should)
+ * try to change the array[] accesses into registers. However, on
+ * machines with less than ~25 registers, that won't really work,
+ * and at least gcc will make an unholy mess of it.
+ *
+ * So to avoid that mess which just slows things down, we force
+ * the stores to memory to actually happen (we might be better off
+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
+ * suggested by Artur Skawina - that will also make gcc unable to
+ * try to do the silly "optimize away loads" part because it won't
+ * see what the value will be).
+ *
+ * Ben Herrenschmidt reports that on PPC, the C version comes close
+ * to the optimized asm with this (ie on PPC you don't want that
+ * 'volatile', since there are lots of registers).
+ *
+ * On ARM we get the best code generation by forcing a full memory barrier
+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
+ * the stack frame size simply explode and performance goes down the drain.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+  #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
+#elif defined(__GNUC__) && defined(__arm__)
+  #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+#else
+  #define setW(x, val) (W(x) = (val))
+#endif
+
+/*
+ * Performance might be improved if the CPU architecture is OK with
+ * unaligned 32-bit loads and a fast ntohl() is available.
+ * Otherwise fall back to byte loads and shifts which is portable,
+ * and is faster on architectures with memory alignment issues.
+ */
+
+#if defined(__i386__) || defined(__x86_64__) || \
+    defined(__ppc__) || defined(__ppc64__) || \
+    defined(__powerpc__) || defined(__powerpc64__) || \
+    defined(__s390__) || defined(__s390x__)
+
+#define get_be32(p)    ntohl(*(unsigned int *)(p))
+#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
+
+#else
+
+#define get_be32(p)    ( \
+       (*((unsigned char *)(p) + 0) << 24) | \
+       (*((unsigned char *)(p) + 1) << 16) | \
+       (*((unsigned char *)(p) + 2) <<  8) | \
+       (*((unsigned char *)(p) + 3) <<  0) )
+#define put_be32(p, v) do { \
+       unsigned int __v = (v); \
+       *((unsigned char *)(p) + 0) = __v >> 24; \
+       *((unsigned char *)(p) + 1) = __v >> 16; \
+       *((unsigned char *)(p) + 2) = __v >>  8; \
+       *((unsigned char *)(p) + 3) = __v >>  0; } while (0)
+
+#endif
+
+/* This "rolls" over the 512-bit array */
+#define W(x) (array[(x)&15])
+
+/*
+ * Where do we get the source from? The first 16 iterations get it from
+ * the input data, the next mix it from the 512-bit array.
+ */
+#define SHA_SRC(t) get_be32(data + t)
+#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
+
+#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
+       unsigned int TEMP = input(t); setW(t, TEMP); \
+       E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
+       B = SHA_ROR(B, 2); } while (0)
+
+#define T_0_15(t, A, B, C, D, E)  SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) ,  0xca62c1d6, A, B, C, D, E )
+
+static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)
+{
+       unsigned int A,B,C,D,E;
+       unsigned int array[16];
+
+       A = ctx->H[0];
+       B = ctx->H[1];
+       C = ctx->H[2];
+       D = ctx->H[3];
+       E = ctx->H[4];
+
+       /* Round 1 - iterations 0-16 take their input from 'data' */
+       T_0_15( 0, A, B, C, D, E);
+       T_0_15( 1, E, A, B, C, D);
+       T_0_15( 2, D, E, A, B, C);
+       T_0_15( 3, C, D, E, A, B);
+       T_0_15( 4, B, C, D, E, A);
+       T_0_15( 5, A, B, C, D, E);
+       T_0_15( 6, E, A, B, C, D);
+       T_0_15( 7, D, E, A, B, C);
+       T_0_15( 8, C, D, E, A, B);
+       T_0_15( 9, B, C, D, E, A);
+       T_0_15(10, A, B, C, D, E);
+       T_0_15(11, E, A, B, C, D);
+       T_0_15(12, D, E, A, B, C);
+       T_0_15(13, C, D, E, A, B);
+       T_0_15(14, B, C, D, E, A);
+       T_0_15(15, A, B, C, D, E);
+
+       /* Round 1 - tail. Input from 512-bit mixing array */
+       T_16_19(16, E, A, B, C, D);
+       T_16_19(17, D, E, A, B, C);
+       T_16_19(18, C, D, E, A, B);
+       T_16_19(19, B, C, D, E, A);
+
+       /* Round 2 */
+       T_20_39(20, A, B, C, D, E);
+       T_20_39(21, E, A, B, C, D);
+       T_20_39(22, D, E, A, B, C);
+       T_20_39(23, C, D, E, A, B);
+       T_20_39(24, B, C, D, E, A);
+       T_20_39(25, A, B, C, D, E);
+       T_20_39(26, E, A, B, C, D);
+       T_20_39(27, D, E, A, B, C);
+       T_20_39(28, C, D, E, A, B);
+       T_20_39(29, B, C, D, E, A);
+       T_20_39(30, A, B, C, D, E);
+       T_20_39(31, E, A, B, C, D);
+       T_20_39(32, D, E, A, B, C);
+       T_20_39(33, C, D, E, A, B);
+       T_20_39(34, B, C, D, E, A);
+       T_20_39(35, A, B, C, D, E);
+       T_20_39(36, E, A, B, C, D);
+       T_20_39(37, D, E, A, B, C);
+       T_20_39(38, C, D, E, A, B);
+       T_20_39(39, B, C, D, E, A);
+
+       /* Round 3 */
+       T_40_59(40, A, B, C, D, E);
+       T_40_59(41, E, A, B, C, D);
+       T_40_59(42, D, E, A, B, C);
+       T_40_59(43, C, D, E, A, B);
+       T_40_59(44, B, C, D, E, A);
+       T_40_59(45, A, B, C, D, E);
+       T_40_59(46, E, A, B, C, D);
+       T_40_59(47, D, E, A, B, C);
+       T_40_59(48, C, D, E, A, B);
+       T_40_59(49, B, C, D, E, A);
+       T_40_59(50, A, B, C, D, E);
+       T_40_59(51, E, A, B, C, D);
+       T_40_59(52, D, E, A, B, C);
+       T_40_59(53, C, D, E, A, B);
+       T_40_59(54, B, C, D, E, A);
+       T_40_59(55, A, B, C, D, E);
+       T_40_59(56, E, A, B, C, D);
+       T_40_59(57, D, E, A, B, C);
+       T_40_59(58, C, D, E, A, B);
+       T_40_59(59, B, C, D, E, A);
+
+       /* Round 4 */
+       T_60_79(60, A, B, C, D, E);
+       T_60_79(61, E, A, B, C, D);
+       T_60_79(62, D, E, A, B, C);
+       T_60_79(63, C, D, E, A, B);
+       T_60_79(64, B, C, D, E, A);
+       T_60_79(65, A, B, C, D, E);
+       T_60_79(66, E, A, B, C, D);
+       T_60_79(67, D, E, A, B, C);
+       T_60_79(68, C, D, E, A, B);
+       T_60_79(69, B, C, D, E, A);
+       T_60_79(70, A, B, C, D, E);
+       T_60_79(71, E, A, B, C, D);
+       T_60_79(72, D, E, A, B, C);
+       T_60_79(73, C, D, E, A, B);
+       T_60_79(74, B, C, D, E, A);
+       T_60_79(75, A, B, C, D, E);
+       T_60_79(76, E, A, B, C, D);
+       T_60_79(77, D, E, A, B, C);
+       T_60_79(78, C, D, E, A, B);
+       T_60_79(79, B, C, D, E, A);
+
+       ctx->H[0] += A;
+       ctx->H[1] += B;
+       ctx->H[2] += C;
+       ctx->H[3] += D;
+       ctx->H[4] += E;
+}
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx)
+{
+       ctx->size = 0;
+
+       /* Initialize H with the magic constants (see FIPS180 for constants) */
+       ctx->H[0] = 0x67452301;
+       ctx->H[1] = 0xefcdab89;
+       ctx->H[2] = 0x98badcfe;
+       ctx->H[3] = 0x10325476;
+       ctx->H[4] = 0xc3d2e1f0;
+}
+
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+{
+       int lenW = ctx->size & 63;
+
+       ctx->size += len;
+
+       /* Read the data into W and process blocks as they get full */
+       if (lenW) {
+               int left = 64 - lenW;
+               if (len < left)
+                       left = len;
+               memcpy(lenW + (char *)ctx->W, data, left);
+               lenW = (lenW + left) & 63;
+               len -= left;
+               data = ((const char *)data + left);
+               if (lenW)
+                       return;
+               blk_SHA1_Block(ctx, ctx->W);
+       }
+       while (len >= 64) {
+               blk_SHA1_Block(ctx, data);
+               data = ((const char *)data + 64);
+               len -= 64;
+       }
+       if (len)
+               memcpy(ctx->W, data, len);
+}
+
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
+{
+       static const unsigned char pad[64] = { 0x80 };
+       unsigned int padlen[2];
+       int i;
+
+       /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+       padlen[0] = htonl(ctx->size >> 29);
+       padlen[1] = htonl(ctx->size << 3);
+
+       i = ctx->size & 63;
+       blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
+       blk_SHA1_Update(ctx, padlen, 8);
+
+       /* Output hash */
+       for (i = 0; i < 5; i++)
+               put_be32(hashout + i*4, ctx->H[i]);
+}
diff --git a/block-sha1/sha1.h b/block-sha1/sha1.h
new file mode 100644 (file)
index 0000000..b864df6
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+typedef struct {
+       unsigned long long size;
+       unsigned int H[5];
+       unsigned int W[16];
+} blk_SHA_CTX;
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx);
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
+
+#define git_SHA_CTX    blk_SHA_CTX
+#define git_SHA1_Init  blk_SHA1_Init
+#define git_SHA1_Update        blk_SHA1_Update
+#define git_SHA1_Final blk_SHA1_Final
index 62030af4b51abbfb9c488dbf018770f3b3606789..05ef3f5c9ce6e1b55a7eb483a197fe9fd6e5437d 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -172,7 +172,7 @@ void create_branch(const char *head,
 
        lock = lock_any_ref_for_update(ref.buf, NULL, 0);
        if (!lock)
-               die("Failed to lock ref for update: %s.", strerror(errno));
+               die_errno("Failed to lock ref for update");
 
        if (reflog)
                log_all_ref_updates = 1;
@@ -188,7 +188,7 @@ void create_branch(const char *head,
                setup_tracking(name, real_ref, track);
 
        if (write_ref_sha1(lock, sha1, msg) < 0)
-               die("Failed to write ref: %s.", strerror(errno));
+               die_errno("Failed to write ref");
 
        strbuf_release(&ref);
        free(real_ref);
index cb67d2c17e4017e8deede61b30c0eb7c8c60ac1e..cb6e5906fb76f2460f212c54b557e5ae0ce23579 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)
@@ -61,7 +63,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
        fill_pathspec_matches(pathspec, seen, specs);
 
        for (i = 0; i < specs; i++) {
-               if (!seen[i] && !file_exists(pathspec[i]))
+               if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i]))
                        die("pathspec '%s' did not match any files",
                                        pathspec[i]);
        }
@@ -95,35 +97,6 @@ static void treat_gitlinks(const char **pathspec)
        }
 }
 
-static void fill_directory(struct dir_struct *dir, const char **pathspec,
-               int ignored_too)
-{
-       const char *path, *base;
-       int baselen;
-
-       /* Set up the default git porcelain excludes */
-       memset(dir, 0, sizeof(*dir));
-       if (!ignored_too) {
-               dir->flags |= DIR_COLLECT_IGNORED;
-               setup_standard_excludes(dir);
-       }
-
-       /*
-        * Calculate common prefix for the pathspec, and
-        * use that to optimize the directory walk
-        */
-       baselen = common_prefix(pathspec);
-       path = ".";
-       base = "";
-       if (baselen)
-               path = base = xmemdupz(*pathspec, baselen);
-
-       /* Read the directory and prune it */
-       read_directory(dir, path, base, baselen, pathspec);
-       if (pathspec)
-               prune_directory(dir, pathspec, baselen);
-}
-
 static void refresh(int verbose, const char **pathspec)
 {
        char *seen;
@@ -132,8 +105,8 @@ static void refresh(int verbose, const char **pathspec)
        for (specs = 0; pathspec[specs];  specs++)
                /* nothing */;
        seen = xcalloc(specs, 1);
-       refresh_index(&the_index, verbose ? REFRESH_SAY_CHANGED : REFRESH_QUIET,
-                     pathspec, seen);
+       refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
+                     pathspec, seen, "Unstaged changes after refreshing the index:");
        for (i = 0; i < specs; i++) {
                if (!seen[i])
                        die("pathspec '%s' did not match any files", pathspec[i]);
@@ -158,27 +131,27 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
        return pathspec;
 }
 
-int interactive_add(int argc, const char **argv, const char *prefix)
+int run_add_interactive(const char *revision, const char *patch_mode,
+                       const char **pathspec)
 {
-       int status, ac;
+       int status, ac, pc = 0;
        const char **args;
-       const char **pathspec = NULL;
 
-       if (argc) {
-               pathspec = validate_pathspec(argc, argv, prefix);
-               if (!pathspec)
-                       return -1;
-       }
+       if (pathspec)
+               while (pathspec[pc])
+                       pc++;
 
-       args = xcalloc(sizeof(const char *), (argc + 4));
+       args = xcalloc(sizeof(const char *), (pc + 5));
        ac = 0;
        args[ac++] = "add--interactive";
-       if (patch_interactive)
-               args[ac++] = "--patch";
+       if (patch_mode)
+               args[ac++] = patch_mode;
+       if (revision)
+               args[ac++] = revision;
        args[ac++] = "--";
-       if (argc) {
-               memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
-               ac += argc;
+       if (pc) {
+               memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
+               ac += pc;
        }
        args[ac] = NULL;
 
@@ -187,6 +160,66 @@ int interactive_add(int argc, const char **argv, const char *prefix)
        return status;
 }
 
+int interactive_add(int argc, const char **argv, const char *prefix)
+{
+       const char **pathspec = NULL;
+
+       if (argc) {
+               pathspec = validate_pathspec(argc, argv, prefix);
+               if (!pathspec)
+                       return -1;
+       }
+
+       return run_add_interactive(NULL,
+                                  patch_interactive ? "--patch" : NULL,
+                                  pathspec);
+}
+
+static 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 = xfdopen(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_errno("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 +234,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 +284,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");
@@ -290,9 +329,21 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                die("index file corrupt");
        treat_gitlinks(pathspec);
 
-       if (add_new_files)
+       if (add_new_files) {
+               int baselen;
+
+               /* Set up the default git porcelain excludes */
+               memset(&dir, 0, sizeof(dir));
+               if (!ignored_too) {
+                       dir.flags |= DIR_COLLECT_IGNORED;
+                       setup_standard_excludes(&dir);
+               }
+
                /* This picks up the paths that are not tracked */
-               fill_directory(&dir, pathspec, ignored_too);
+               baselen = fill_directory(&dir, pathspec);
+               if (pathspec)
+                       prune_directory(&dir, pathspec, baselen);
+       }
 
        if (refresh_only) {
                refresh(verbose, pathspec);
index 7b404ef6601e5b7820e9b1b6812225639e3ea4b1..f667368d161609ea3c93fc6ebe2452b9cd0e557d 100644 (file)
@@ -61,6 +61,13 @@ static enum ws_error_action {
 static int whitespace_error;
 static int squelch_whitespace_errors = 5;
 static int applied_after_fixing_ws;
+
+static enum ws_ignore {
+       ignore_ws_none,
+       ignore_ws_change,
+} ws_ignore_action = ignore_ws_none;
+
+
 static const char *patch_input_file;
 static const char *root;
 static int root_len;
@@ -97,6 +104,21 @@ static void parse_whitespace_option(const char *option)
        die("unrecognized whitespace option '%s'", option);
 }
 
+static void parse_ignorewhitespace_option(const char *option)
+{
+       if (!option || !strcmp(option, "no") ||
+           !strcmp(option, "false") || !strcmp(option, "never") ||
+           !strcmp(option, "none")) {
+               ws_ignore_action = ignore_ws_none;
+               return;
+       }
+       if (!strcmp(option, "change")) {
+               ws_ignore_action = ignore_ws_change;
+               return;
+       }
+       die("unrecognized whitespace ignore option '%s'", option);
+}
+
 static void set_default_whitespace_mode(const char *whitespace_option)
 {
        if (!whitespace_option && !apply_default_whitespace)
@@ -131,6 +153,7 @@ struct fragment {
        const char *patch;
        int size;
        int rejected;
+       int linenr;
        struct fragment *next;
 };
 
@@ -214,6 +237,62 @@ static uint32_t hash_line(const char *cp, size_t len)
        return h;
 }
 
+/*
+ * Compare lines s1 of length n1 and s2 of length n2, ignoring
+ * whitespace difference. Returns 1 if they match, 0 otherwise
+ */
+static int fuzzy_matchlines(const char *s1, size_t n1,
+                           const char *s2, size_t n2)
+{
+       const char *last1 = s1 + n1 - 1;
+       const char *last2 = s2 + n2 - 1;
+       int result = 0;
+
+       if (n1 < 0 || n2 < 0)
+               return 0;
+
+       /* ignore line endings */
+       while ((*last1 == '\r') || (*last1 == '\n'))
+               last1--;
+       while ((*last2 == '\r') || (*last2 == '\n'))
+               last2--;
+
+       /* skip leading whitespace */
+       while (isspace(*s1) && (s1 <= last1))
+               s1++;
+       while (isspace(*s2) && (s2 <= last2))
+               s2++;
+       /* early return if both lines are empty */
+       if ((s1 > last1) && (s2 > last2))
+               return 1;
+       while (!result) {
+               result = *s1++ - *s2++;
+               /*
+                * Skip whitespace inside. We check for whitespace on
+                * both buffers because we don't want "a b" to match
+                * "ab"
+                */
+               if (isspace(*s1) && isspace(*s2)) {
+                       while (isspace(*s1) && s1 <= last1)
+                               s1++;
+                       while (isspace(*s2) && s2 <= last2)
+                               s2++;
+               }
+               /*
+                * If we reached the end on one side only,
+                * lines don't match
+                */
+               if (
+                   ((s2 > last2) && (s1 <= last1)) ||
+                   ((s1 > last1) && (s2 <= last2)))
+                       return 0;
+               if ((s1 > last1) && (s2 > last2))
+                       break;
+       }
+
+       return !result;
+}
+
 static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
 {
        ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
@@ -280,7 +359,7 @@ static void say_patch_name(FILE *output, const char *pre,
 static void read_patch_file(struct strbuf *sb, int fd)
 {
        if (strbuf_read(sb, fd, 0) < 0)
-               die("git apply: read returned %s", strerror(errno));
+               die_errno("git apply: failed to read");
 
        /*
         * Make sure that we have some slop in the buffer
@@ -320,6 +399,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 +442,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 +462,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 +476,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 +485,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)
@@ -443,6 +536,76 @@ static int guess_p_value(const char *nameline)
        return val;
 }
 
+/*
+ * Does the ---/+++ line has the POSIX timestamp after the last HT?
+ * GNU diff puts epoch there to signal a creation/deletion event.  Is
+ * this such a timestamp?
+ */
+static int has_epoch_timestamp(const char *nameline)
+{
+       /*
+        * We are only interested in epoch timestamp; any non-zero
+        * fraction cannot be one, hence "(\.0+)?" in the regexp below.
+        * For the same reason, the date must be either 1969-12-31 or
+        * 1970-01-01, and the seconds part must be "00".
+        */
+       const char stamp_regexp[] =
+               "^(1969-12-31|1970-01-01)"
+               " "
+               "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
+               " "
+               "([-+][0-2][0-9][0-5][0-9])\n";
+       const char *timestamp = NULL, *cp;
+       static regex_t *stamp;
+       regmatch_t m[10];
+       int zoneoffset;
+       int hourminute;
+       int status;
+
+       for (cp = nameline; *cp != '\n'; cp++) {
+               if (*cp == '\t')
+                       timestamp = cp + 1;
+       }
+       if (!timestamp)
+               return 0;
+       if (!stamp) {
+               stamp = xmalloc(sizeof(*stamp));
+               if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
+                       warning("Cannot prepare timestamp regexp %s",
+                               stamp_regexp);
+                       return 0;
+               }
+       }
+
+       status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
+       if (status) {
+               if (status != REG_NOMATCH)
+                       warning("regexec returned %d for input: %s",
+                               status, timestamp);
+               return 0;
+       }
+
+       zoneoffset = strtol(timestamp + m[3].rm_so + 1, NULL, 10);
+       zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
+       if (timestamp[m[3].rm_so] == '-')
+               zoneoffset = -zoneoffset;
+
+       /*
+        * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
+        * (west of GMT) or 1970-01-01 (east of GMT)
+        */
+       if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
+           (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
+               return 0;
+
+       hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
+                     strtol(timestamp + 14, NULL, 10) -
+                     zoneoffset);
+
+       return ((zoneoffset < 0 && hourminute == 1440) ||
+               (0 <= zoneoffset && !hourminute));
+}
+
 /*
  * Get the name etc info from the ---/+++ lines of a traditional patch header
  *
@@ -479,7 +642,17 @@ static void parse_traditional_patch(const char *first, const char *second, struc
        } else {
                name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
                name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);
-               patch->old_name = patch->new_name = name;
+               if (has_epoch_timestamp(first)) {
+                       patch->is_new = 1;
+                       patch->is_delete = 0;
+                       patch->new_name = name;
+               } else if (has_epoch_timestamp(second)) {
+                       patch->is_new = 0;
+                       patch->is_delete = 1;
+                       patch->old_name = name;
+               } else {
+                       patch->old_name = patch->new_name = name;
+               }
        }
        if (!name)
                die("unable to find filename in patch at line %d", linenr);
@@ -1055,23 +1228,29 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
        return -1;
 }
 
-static void check_whitespace(const char *line, int len, unsigned ws_rule)
+static void record_ws_error(unsigned result, const char *line, int len, int linenr)
 {
        char *err;
-       unsigned result = ws_check(line + 1, len - 1, ws_rule);
+
        if (!result)
                return;
 
        whitespace_error++;
        if (squelch_whitespace_errors &&
            squelch_whitespace_errors < whitespace_error)
-               ;
-       else {
-               err = whitespace_error_string(result);
-               fprintf(stderr, "%s:%d: %s.\n%.*s\n",
-                       patch_input_file, linenr, err, len - 2, line + 1);
-               free(err);
-       }
+               return;
+
+       err = whitespace_error_string(result);
+       fprintf(stderr, "%s:%d: %s.\n%.*s\n",
+               patch_input_file, linenr, err, len, line);
+       free(err);
+}
+
+static void check_whitespace(const char *line, int len, unsigned ws_rule)
+{
+       unsigned result = ws_check(line + 1, len - 1, ws_rule);
+
+       record_ws_error(result, line + 1, len - 2, linenr);
 }
 
 /*
@@ -1187,6 +1366,7 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                int len;
 
                fragment = xcalloc(1, sizeof(*fragment));
+               fragment->linenr = linenr;
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
                        die("corrupt patch at line %d", linenr);
@@ -1578,10 +1758,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
        }
 }
 
+/*
+ * Update the preimage, and the common lines in postimage,
+ * from buffer buf of length len. If postlen is 0 the postimage
+ * is updated in place, otherwise it's updated on a new buffer
+ * of length postlen
+ */
+
 static void update_pre_post_images(struct image *preimage,
                                   struct image *postimage,
                                   char *buf,
-                                  size_t len)
+                                  size_t len, size_t postlen)
 {
        int i, ctx;
        char *new, *old, *fixed;
@@ -1600,11 +1787,19 @@ static void update_pre_post_images(struct image *preimage,
        *preimage = fixed_preimage;
 
        /*
-        * Adjust the common context lines in postimage, in place.
-        * This is possible because whitespace fixing does not make
-        * the string grow.
+        * Adjust the common context lines in postimage. This can be
+        * done in-place when we are just doing whitespace fixing,
+        * which does not make the string grow, but needs a new buffer
+        * when ignoring whitespace causes the update, since in this case
+        * we could have e.g. tabs converted to multiple spaces.
+        * We trust the caller to tell us if the update can be done
+        * in place (postlen==0) or not.
         */
-       new = old = postimage->buf;
+       old = postimage->buf;
+       if (postlen)
+               new = postimage->buf = xmalloc(postlen);
+       else
+               new = old;
        fixed = preimage->buf;
        for (i = ctx = 0; i < postimage->nr; i++) {
                size_t len = postimage->line[i].len;
@@ -1679,12 +1874,56 @@ static int match_fragment(struct image *img,
            !memcmp(img->buf + try, preimage->buf, preimage->len))
                return 1;
 
+       /*
+        * No exact match. If we are ignoring whitespace, run a line-by-line
+        * fuzzy matching. We collect all the line length information because
+        * we need it to adjust whitespace if we match.
+        */
+       if (ws_ignore_action == ignore_ws_change) {
+               size_t imgoff = 0;
+               size_t preoff = 0;
+               size_t postlen = postimage->len;
+               for (i = 0; i < preimage->nr; i++) {
+                       size_t prelen = preimage->line[i].len;
+                       size_t imglen = img->line[try_lno+i].len;
+
+                       if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
+                                             preimage->buf + preoff, prelen))
+                               return 0;
+                       if (preimage->line[i].flag & LINE_COMMON)
+                               postlen += imglen - prelen;
+                       imgoff += imglen;
+                       preoff += prelen;
+               }
+
+               /*
+                * Ok, the preimage matches with whitespace fuzz. Update it and
+                * the common postimage lines to use the same whitespace as the
+                * target. imgoff now holds the true length of the target that
+                * matches the preimage, and we need to update the line lengths
+                * of the preimage to match the target ones.
+                */
+               fixed_buf = xmalloc(imgoff);
+               memcpy(fixed_buf, img->buf + try, imgoff);
+               for (i = 0; i < preimage->nr; i++)
+                       preimage->line[i].len = img->line[try_lno+i].len;
+
+               /*
+                * Update the preimage buffer and the postimage context lines.
+                */
+               update_pre_post_images(preimage, postimage,
+                               fixed_buf, imgoff, postlen);
+               return 1;
+       }
+
        if (ws_error_action != correct_ws_error)
                return 0;
 
        /*
         * The hunk does not apply byte-by-byte, but the hash says
-        * it might with whitespace fuzz.
+        * it might with whitespace fuzz. We haven't been asked to
+        * ignore whitespace, we were asked to correct whitespace
+        * errors, so let's try matching after whitespace correction.
         */
        fixed_buf = xmalloc(preimage->len + 1);
        buf = fixed_buf;
@@ -1736,7 +1975,7 @@ static int match_fragment(struct image *img,
         * hunk match.  Update the context lines in the postimage.
         */
        update_pre_post_images(preimage, postimage,
-                              fixed_buf, buf - fixed_buf);
+                              fixed_buf, buf - fixed_buf, 0);
        return 1;
 
  unmatch_exit:
@@ -1911,6 +2150,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                int len = linelen(patch, size);
                int plen, added;
                int added_blank_line = 0;
+               int is_blank_context = 0;
 
                if (!len)
                        break;
@@ -1943,8 +2183,12 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        *new++ = '\n';
                        add_line_info(&preimage, "\n", 1, LINE_COMMON);
                        add_line_info(&postimage, "\n", 1, LINE_COMMON);
+                       is_blank_context = 1;
                        break;
                case ' ':
+                       if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
+                           ws_blank_line(patch + 1, plen, ws_rule))
+                               is_blank_context = 1;
                case '-':
                        memcpy(old, patch + 1, plen);
                        add_line_info(&preimage, old, plen,
@@ -1971,7 +2215,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                                      (first == '+' ? 0 : LINE_COMMON));
                        new += added;
                        if (first == '+' &&
-                           added == 1 && new[-1] == '\n')
+                           (ws_rule & WS_BLANK_AT_EOF) &&
+                           ws_blank_line(patch + 1, plen, ws_rule))
                                added_blank_line = 1;
                        break;
                case '@': case '\\':
@@ -1984,6 +2229,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                }
                if (added_blank_line)
                        new_blank_lines_at_end++;
+               else if (is_blank_context)
+                       ;
                else
                        new_blank_lines_at_end = 0;
                patch += len;
@@ -2065,17 +2312,24 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        }
 
        if (applied_pos >= 0) {
-               if (ws_error_action == correct_ws_error &&
-                   new_blank_lines_at_end &&
-                   postimage.nr + applied_pos == img->nr) {
+               if (new_blank_lines_at_end &&
+                   preimage.nr + applied_pos == img->nr &&
+                   (ws_rule & WS_BLANK_AT_EOF) &&
+                   ws_error_action != nowarn_ws_error) {
+                       record_ws_error(WS_BLANK_AT_EOF, "+", 1, frag->linenr);
+                       if (ws_error_action == correct_ws_error) {
+                               while (new_blank_lines_at_end--)
+                                       remove_last_line(&postimage);
+                       }
                        /*
-                        * If the patch application adds blank lines
-                        * at the end, and if the patch applies at the
-                        * end of the image, remove those added blank
-                        * lines.
+                        * We would want to prevent write_out_results()
+                        * from taking place in apply_patch() that follows
+                        * the callchain led us here, which is:
+                        * apply_patch->check_patch_list->check_patch->
+                        * apply_data->apply_fragments->apply_one_fragment
                         */
-                       while (new_blank_lines_at_end--)
-                               remove_last_line(&postimage);
+                       if (ws_error_action == die_on_ws_error)
+                               apply = 0;
                }
 
                /*
@@ -2600,7 +2854,7 @@ static int get_current_sha1(const char *path, unsigned char *sha1)
 static void build_fake_ancestor(struct patch *list, const char *filename)
 {
        struct patch *patch;
-       struct index_state result = { 0 };
+       struct index_state result = { NULL };
        int fd;
 
        /* Once we start supporting the reverse patch, it may be
@@ -2781,7 +3035,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
                        if (rmdir(patch->old_name))
                                warning("unable to remove submodule %s",
                                        patch->old_name);
-               } else if (!unlink(patch->old_name) && rmdir_empty) {
+               } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
                        remove_path(patch->old_name);
                }
        }
@@ -2809,8 +3063,8 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
        } else {
                if (!cached) {
                        if (lstat(path, &st) < 0)
-                               die("unable to stat newly created file %s",
-                                   path);
+                               die_errno("unable to stat newly created file '%s'",
+                                         path);
                        fill_stat_cache_info(ce, &st);
                }
                if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
@@ -2850,7 +3104,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        strbuf_release(&nbuf);
 
        if (close(fd) < 0)
-               die("closing file %s: %s", path, strerror(errno));
+               die_errno("closing file '%s'", path);
        return 0;
 }
 
@@ -2891,7 +3145,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                        if (!try_create_file(newpath, mode, buf, size)) {
                                if (!rename(newpath, path))
                                        return;
-                               unlink(newpath);
+                               unlink_or_warn(newpath);
                                break;
                        }
                        if (errno != EEXIST)
@@ -2899,7 +3153,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                        ++nr;
                }
        }
-       die("unable to write file %s mode %o", path, mode);
+       die_errno("unable to write file '%s' mode %o", path, mode);
 }
 
 static void create_file(struct patch *patch)
@@ -3178,6 +3432,8 @@ static int git_apply_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "apply.whitespace"))
                return git_config_string(&apply_default_whitespace, var, value);
+       else if (!strcmp(var, "apply.ignorewhitespace"))
+               return git_config_string(&apply_default_ignorewhitespace, var, value);
        return git_default_config(var, value, cb);
 }
 
@@ -3214,6 +3470,16 @@ static int option_parse_z(const struct option *opt,
        return 0;
 }
 
+static int option_parse_space_change(const struct option *opt,
+                         const char *arg, int unset)
+{
+       if (unset)
+               ws_ignore_action = ignore_ws_none;
+       else
+               ws_ignore_action = ignore_ws_change;
+       return 0;
+}
+
 static int option_parse_whitespace(const struct option *opt,
                                   const char *arg, int unset)
 {
@@ -3263,9 +3529,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 +3546,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",
@@ -3288,6 +3556,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
                        "detect new or modified lines that have whitespace errors",
                        0, option_parse_whitespace },
+               { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
+                       "ignore changes in whitespace when finding context",
+                       PARSE_OPT_NOARG, option_parse_space_change },
+               { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
+                       "ignore changes in whitespace when finding context",
+                       PARSE_OPT_NOARG, option_parse_space_change },
                OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
                        "apply the patch in reverse"),
                OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
@@ -3312,9 +3586,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
        git_config(git_apply_config, NULL);
        if (apply_default_whitespace)
                parse_whitespace_option(apply_default_whitespace);
+       if (apply_default_ignorewhitespace)
+               parse_ignorewhitespace_option(apply_default_ignorewhitespace);
 
-       argc = parse_options(argc, argv, builtin_apply_options,
+       argc = parse_options(argc, argv, prefix, builtin_apply_options,
                        apply_usage, 0);
+
        if (apply_with_reject)
                apply = apply_verbosely = 1;
        if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
@@ -3339,7 +3616,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
 
                fd = open(arg, O_RDONLY);
                if (fd < 0)
-                       die("can't open patch '%s': %s", arg, strerror(errno));
+                       die_errno("can't open patch '%s'", arg);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
                errs |= apply_patch(fd, arg, options);
index ab50cebba0e6798996cc007ced9079c3f0b94a29..12351e9dd5c481fddc8db07c00a211b4620250bd 100644 (file)
@@ -13,10 +13,10 @@ static void create_output_file(const char *output_file)
 {
        int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
        if (output_fd < 0)
-               die("could not create archive file: %s ", output_file);
+               die_errno("could not create archive file '%s'", output_file);
        if (output_fd != 1) {
                if (dup2(output_fd, 1) < 0)
-                       die("could not redirect output");
+                       die_errno("could not redirect output");
                else
                        close(output_fd);
        }
@@ -60,6 +60,17 @@ static int run_remote_archiver(int argc, const char **argv,
        return !!rv;
 }
 
+static const char *format_from_name(const char *filename)
+{
+       const char *ext = strrchr(filename, '.');
+       if (!ext)
+               return NULL;
+       ext++;
+       if (!strcasecmp(ext, "zip"))
+               return "zip";
+       return NULL;
+}
+
 #define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH |         \
                             PARSE_OPT_KEEP_ARGV0 |     \
                             PARSE_OPT_KEEP_UNKNOWN |   \
@@ -70,20 +81,39 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
        const char *exec = "git-upload-archive";
        const char *output = NULL;
        const char *remote = NULL;
+       const char *format = NULL;
        struct option local_opts[] = {
-               OPT_STRING(0, "output", &output, "file",
+               OPT_STRING('o', "output", &output, "file",
                        "write the archive to this file"),
                OPT_STRING(0, "remote", &remote, "repo",
                        "retrieve the archive from remote repository <repo>"),
                OPT_STRING(0, "exec", &exec, "cmd",
                        "path to the remote git-upload-archive command"),
+               OPT_STRING(0, "format", &format, "fmt", "archive format"),
                OPT_END()
        };
+       char fmt_opt[32];
 
-       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)
+       if (output) {
                create_output_file(output);
+               if (!format)
+                       format = format_from_name(output);
+       }
+
+       if (format) {
+               sprintf(fmt_opt, "--format=%s", format);
+               /*
+                * This is safe because either --format and/or --output must
+                * have been given on the original command line if we get to
+                * this point, and parse_options() must have eaten at least
+                * one argument, i.e. we have enough room to append to argv[].
+                */
+               argv[argc++] = fmt_opt;
+               argv[argc] = NULL;
+       }
 
        if (remote)
                return run_remote_archiver(argc, argv, remote, exec);
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 cf74a926149668b7e67544aecd46a0e05d4f59d9..7512773b401255e76aadd5be6e561432d7d60772 100644 (file)
@@ -362,18 +362,28 @@ static struct origin *find_origin(struct scoreboard *sb,
                               "", &diff_opts);
        diffcore_std(&diff_opts);
 
-       /* It is either one entry that says "modified", or "created",
-        * or nothing.
-        */
        if (!diff_queued_diff.nr) {
                /* The path is the same as parent */
                porigin = get_origin(sb, parent, origin->path);
                hashcpy(porigin->blob_sha1, origin->blob_sha1);
-       }
-       else if (diff_queued_diff.nr != 1)
-               die("internal error in blame::find_origin");
-       else {
-               struct diff_filepair *p = diff_queued_diff.queue[0];
+       } else {
+               /*
+                * Since origin->path is a pathspec, if the parent
+                * commit had it as a directory, we will see a whole
+                * bunch of deletion of files in the directory that we
+                * do not care about.
+                */
+               int i;
+               struct diff_filepair *p = NULL;
+               for (i = 0; i < diff_queued_diff.nr; i++) {
+                       const char *name;
+                       p = diff_queued_diff.queue[i];
+                       name = p->one->path ? p->one->path : p->two->path;
+                       if (!strcmp(name, origin->path))
+                               break;
+               }
+               if (!p)
+                       die("internal error in blame::find_origin");
                switch (p->status) {
                default:
                        die("internal error in blame::find_origin (%c)",
@@ -1338,7 +1348,7 @@ static void get_ac_line(const char *inbuf, const char *what,
        /*
         * Now, convert both name and e-mail using mailmap
         */
-       if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
+       if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
                /* Add a trailing '>' to email, since map_user returns plain emails
                   Note: It already has '<', since we replace from mail+1 */
                mailpos = memchr(mail, '\0', mail_len);
@@ -1998,23 +2008,23 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
 
                if (contents_from) {
                        if (stat(contents_from, &st) < 0)
-                               die("Cannot stat %s", contents_from);
+                               die_errno("Cannot stat '%s'", contents_from);
                        read_from = contents_from;
                }
                else {
                        if (lstat(path, &st) < 0)
-                               die("Cannot lstat %s", path);
+                               die_errno("Cannot lstat '%s'", path);
                        read_from = path;
                }
                mode = canon_mode(st.st_mode);
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
-                               die("cannot open or read %s", read_from);
+                               die_errno("cannot open or read '%s'", read_from);
                        break;
                case S_IFLNK:
                        if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
-                               die("cannot readlink %s", read_from);
+                               die_errno("cannot readlink '%s'", read_from);
                        break;
                default:
                        die("unsupported file type %s", read_from);
@@ -2025,7 +2035,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
                contents_from = "standard input";
                mode = 0;
                if (strbuf_read(&buf, 0, 0) < 0)
-                       die("read error %s from stdin", strerror(errno));
+                       die_errno("failed to read from stdin");
        }
        convert_to_git(path, buf.buf, buf.len, &buf, 0);
        origin->file.ptr = buf.buf;
@@ -2229,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)) {
@@ -2251,8 +2261,7 @@ parse_done:
        argc = parse_options_end(&ctx);
 
        if (revs_file && read_ancestry(revs_file))
-               die("reading graft file %s failed: %s",
-                   revs_file, strerror(errno));
+               die_errno("reading graft file '%s' failed", revs_file);
 
        if (cmd_is_annotate) {
                output_option |= OUTPUT_ANNOTATE_COMPAT;
@@ -2340,7 +2349,7 @@ parse_done:
 
                setup_work_tree();
                if (!has_string_in_work_tree(path))
-                       die("cannot stat path %s: %s", path, strerror(errno));
+                       die_errno("cannot stat path '%s'", path);
        }
 
        setup_revisions(argc, argv, &revs, NULL);
index 91098ca9b106239916af000cb54a4bf09629e6b6..9f57992062314a3d8d168b5cfb6b3c6737605033 100644 (file)
@@ -191,7 +191,7 @@ struct ref_item {
 
 struct ref_list {
        struct rev_info revs;
-       int index, alloc, maxwidth;
+       int index, alloc, maxwidth, verbose, abbrev;
        struct ref_item *list;
        struct commit_list *with_commit;
        int kinds;
@@ -240,21 +240,24 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        if (ARRAY_SIZE(ref_kind) <= i)
                return 0;
 
-       commit = lookup_commit_reference_gently(sha1, 1);
-       if (!commit)
-               return error("branch '%s' does not point at a commit", refname);
-
-       /* Filter with with_commit if specified */
-       if (!is_descendant_of(commit, ref_list->with_commit))
-               return 0;
-
        /* Don't add types the caller doesn't want */
        if ((kind & ref_list->kinds) == 0)
                return 0;
 
-       if (merge_filter != NO_FILTER)
-               add_pending_object(&ref_list->revs,
-                                  (struct object *)commit, refname);
+       commit = NULL;
+       if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
+               commit = lookup_commit_reference_gently(sha1, 1);
+               if (!commit)
+                       return error("branch '%s' does not point at a commit", refname);
+
+               /* Filter with with_commit if specified */
+               if (!is_descendant_of(commit, ref_list->with_commit))
+                       return 0;
+
+               if (merge_filter != NO_FILTER)
+                       add_pending_object(&ref_list->revs,
+                                          (struct object *)commit, refname);
+       }
 
        /* Resize buffer */
        if (ref_list->index >= ref_list->alloc) {
@@ -415,18 +418,38 @@ static int calc_maxwidth(struct ref_list *refs)
        return w;
 }
 
+
+static void show_detached(struct ref_list *ref_list)
+{
+       struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
+
+       if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
+               struct ref_item item;
+               item.name = xstrdup("(no branch)");
+               item.len = strlen(item.name);
+               item.kind = REF_LOCAL_BRANCH;
+               item.dest = NULL;
+               item.commit = head_commit;
+               if (item.len > ref_list->maxwidth)
+                       ref_list->maxwidth = item.len;
+               print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
+               free(item.name);
+       }
+}
+
 static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
 {
        int i;
        struct ref_list ref_list;
-       struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
 
        memset(&ref_list, 0, sizeof(ref_list));
        ref_list.kinds = kinds;
+       ref_list.verbose = verbose;
+       ref_list.abbrev = abbrev;
        ref_list.with_commit = with_commit;
        if (merge_filter != NO_FILTER)
                init_revisions(&ref_list.revs, NULL);
-       for_each_ref(append_ref, &ref_list);
+       for_each_rawref(append_ref, &ref_list);
        if (merge_filter != NO_FILTER) {
                struct commit *filter;
                filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -442,19 +465,8 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
        detached = (detached && (kinds & REF_LOCAL_BRANCH));
-       if (detached && head_commit &&
-           is_descendant_of(head_commit, with_commit)) {
-               struct ref_item item;
-               item.name = xstrdup("(no branch)");
-               item.len = strlen(item.name);
-               item.kind = REF_LOCAL_BRANCH;
-               item.dest = NULL;
-               item.commit = head_commit;
-               if (item.len > ref_list.maxwidth)
-                       ref_list.maxwidth = item.len;
-               print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1, "");
-               free(item.name);
-       }
+       if (detached)
+               show_detached(&ref_list);
 
        for (i = 0; i < ref_list.index; i++) {
                int current = !detached &&
@@ -547,7 +559,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",
@@ -574,7 +586,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
                OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
                OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
-               OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
+               OPT_BOOLEAN('f', "force", &force_create, "force creation (when already exists)"),
                {
                        OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
                        "commit", "print only not merged branches",
@@ -610,7 +622,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 9b58152047baebfdd6f26ffab31d49347e0cb069..2006cc5cd5cc4d381189f5cf7bdd09fcdd66b9ca 100644 (file)
@@ -9,7 +9,11 @@
  * bundle supporting "fetch", "pull", and "ls-remote".
  */
 
-static const char *bundle_usage="git bundle (create <bundle> <git rev-list args> | verify <bundle> | list-heads <bundle> [refname]... | unbundle <bundle> [refname]... )";
+static const char builtin_bundle_usage[] =
+  "git bundle create <file> <git-rev-list args>\n"
+  "   or: git bundle verify <file>\n"
+  "   or: git bundle list-heads <file> [refname...]\n"
+  "   or: git bundle unbundle <file> [refname...]";
 
 int cmd_bundle(int argc, const char **argv, const char *prefix)
 {
@@ -20,7 +24,7 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
        char buffer[PATH_MAX];
 
        if (argc < 3)
-               usage(bundle_usage);
+               usage(builtin_bundle_usage);
 
        cmd = argv[1];
        bundle_file = argv[2];
@@ -59,5 +63,5 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
                return !!unbundle(&header, bundle_fd) ||
                        list_bundle_refs(&header, argc, argv);
        } else
-               usage(bundle_usage);
+               usage(builtin_bundle_usage);
 }
index 8fad19daedef8a38674ee35cd543983bad610857..590684200854ad6a71653f30d494eb191fd4a324 100644 (file)
@@ -201,8 +201,8 @@ static int batch_objects(int print_contents)
 }
 
 static const char * const cat_file_usage[] = {
-       "git cat-file [-t|-s|-e|-p|<type>] <sha1>",
-       "git cat-file [--batch|--batch-check] < <list_of_sha1s>",
+       "git cat-file (-t|-s|-e|-p|<type>) <object>",
+       "git cat-file (--batch|--batch-check) < <list_of_objects>",
        NULL
 };
 
@@ -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 15f0c32c7a7ed62afe31e3bafb2d7306ba4a3402..d050c3789fcdaf1b50c12a20c28cbeaf774048eb 100644 (file)
@@ -216,7 +216,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
        newfd = hold_locked_index(lock_file, 1);
-       if (read_cache() < 0)
+       if (read_cache_preload(pathspec) < 0)
                return error("corrupt index file");
 
        if (source_tree)
@@ -365,17 +365,14 @@ static int merge_working_tree(struct checkout_opts *opts,
        int ret;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
        int newfd = hold_locked_index(lock_file, 1);
-       int reprime_cache_tree = 0;
 
-       if (read_cache() < 0)
+       if (read_cache_preload(NULL) < 0)
                return error("corrupt index file");
 
-       cache_tree_free(&active_cache_tree);
        if (opts->force) {
                ret = reset_tree(new->commit->tree, opts, 1);
                if (ret)
                        return ret;
-               reprime_cache_tree = 1;
        } else {
                struct tree_desc trees[2];
                struct tree *tree;
@@ -405,15 +402,15 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                topts.dir->flags |= DIR_SHOW_IGNORED;
                topts.dir->exclude_per_dir = ".gitignore";
-               tree = parse_tree_indirect(old->commit->object.sha1);
+               tree = parse_tree_indirect(old->commit ?
+                                          old->commit->object.sha1 :
+                                          (unsigned char *)EMPTY_TREE_SHA1_BIN);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
                tree = parse_tree_indirect(new->commit->object.sha1);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
 
                ret = unpack_trees(2, trees, &topts);
-               if (ret != -1) {
-                       reprime_cache_tree = 1;
-               } else {
+               if (ret == -1) {
                        /*
                         * Unpack couldn't do a trivial merge; either
                         * give up or do a real merge, depending on
@@ -457,8 +454,6 @@ static int merge_working_tree(struct checkout_opts *opts,
                }
        }
 
-       if (reprime_cache_tree)
-               prime_cache_tree(&active_cache_tree, new->commit->tree);
        if (write_cache(newfd, active_cache, active_nr) ||
            commit_locked_index(lock_file))
                die("unable to write new index file");
@@ -548,26 +543,18 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
                parse_commit(new->commit);
        }
 
+       ret = merge_working_tree(opts, &old, new);
+       if (ret)
+               return ret;
+
        /*
-        * If we were on a detached HEAD, but we are now moving to
+        * If we were on a detached HEAD, but have now moved to
         * a new commit, we want to mention the old commit once more
         * to remind the user that it might be lost.
         */
        if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
                describe_detached_head("Previous HEAD position was", old.commit);
 
-       if (!old.commit && !opts->force) {
-               if (!opts->quiet) {
-                       warning("You appear to be on a branch yet to be born.");
-                       warning("Forcing checkout of %s.", new->name);
-               }
-               opts->force = 1;
-       }
-
-       ret = merge_working_tree(opts, &old, new);
-       if (ret)
-               return ret;
-
        update_refs_for_switch(opts, &old, new);
 
        ret = post_checkout_hook(old.commit, new->commit, 1);
@@ -579,6 +566,13 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
        return git_xmerge_config(var, value, cb);
 }
 
+static int interactive_checkout(const char *revision, const char **pathspec,
+                               struct checkout_opts *opts)
+{
+       return run_add_interactive(revision, "--patch=checkout", pathspec);
+}
+
+
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
        struct checkout_opts opts;
@@ -587,6 +581,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        struct branch_info new;
        struct tree *source_tree = NULL;
        char *conflict_style = NULL;
+       int patch_mode = 0;
        struct option options[] = {
                OPT__QUIET(&opts.quiet),
                OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
@@ -597,10 +592,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                            2),
                OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
                            3),
-               OPT_BOOLEAN('f', NULL, &opts.force, "force"),
+               OPT_BOOLEAN('f', "force", &opts.force, "force"),
                OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
                OPT_STRING(0, "conflict", &conflict_style, "style",
                           "conflict style (merge or diff3)"),
+               OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
                OPT_END(),
        };
        int has_dash_dash;
@@ -612,9 +608,13 @@ 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);
 
+       if (patch_mode && (opts.track > 0 || opts.new_branch
+                          || opts.new_branch_log || opts.merge || opts.force))
+               die ("--patch is incompatible with all other options");
+
        /* --track without -b should DWIM */
        if (0 < opts.track && !opts.new_branch) {
                const char *argv0 = argv[0];
@@ -721,6 +721,9 @@ no_reference:
                if (!pathspec)
                        die("invalid path specification");
 
+               if (patch_mode)
+                       return interactive_checkout(new.name, pathspec, &opts);
+
                /* Checkout paths */
                if (opts.new_branch) {
                        if (argc == 1) {
@@ -736,6 +739,9 @@ no_reference:
                return checkout_paths(source_tree, pathspec, &opts);
        }
 
+       if (patch_mode)
+               return interactive_checkout(new.name, NULL, &opts);
+
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
                if (strbuf_check_branch_ref(&buf, opts.new_branch))
index c5ad33d3e665f6d1613df2dfba235b03765565ac..28cdcd0274dd5891c7ad3dc3804cb0acead883f2 100644 (file)
@@ -31,9 +31,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        int i;
        int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
        int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
+       int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
        struct strbuf directory = STRBUF_INIT;
        struct dir_struct dir;
-       const char *path, *base;
        static const char **pathspec;
        struct strbuf buf = STRBUF_INIT;
        const char *qname;
@@ -41,7 +41,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT__QUIET(&quiet),
                OPT__DRY_RUN(&show_only),
-               OPT_BOOLEAN('f', NULL, &force, "force"),
+               OPT_BOOLEAN('f', "force", &force, "force"),
                OPT_BOOLEAN('d', NULL, &remove_directories,
                                "remove whole directories"),
                OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
@@ -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)
@@ -69,6 +70,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                die("clean.requireForce%s set and -n or -f not given; "
                    "refusing to clean", config_set ? "" : " not");
 
+       if (force > 1)
+               rm_flags = 0;
+
        dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
        if (!ignored)
@@ -77,16 +81,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        pathspec = get_pathspec(prefix, argv);
        read_cache();
 
-       /*
-        * Calculate common prefix for the pathspec, and
-        * use that to optimize the directory walk
-        */
-       baselen = common_prefix(pathspec);
-       path = ".";
-       base = "";
-       if (baselen)
-               path = base = xmemdupz(*pathspec, baselen);
-       read_directory(&dir, path, base, baselen, pathspec);
+       fill_directory(&dir, pathspec);
 
        if (pathspec)
                seen = xmalloc(argc > 0 ? argc : 1);
@@ -140,7 +135,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                                   (matches == MATCHED_EXACTLY)) {
                                if (!quiet)
                                        printf("Removing %s\n", qname);
-                               if (remove_dir_recursively(&directory, 0) != 0) {
+                               if (remove_dir_recursively(&directory,
+                                                          rm_flags) != 0) {
                                        warning("failed to remove '%s'", qname);
                                        errors++;
                                }
index 880373f279dd073ea7f0ae8564daca1cc687e529..5762a6f9d8191c79d4fd66d227038d59ab1e785e 100644 (file)
@@ -38,9 +38,10 @@ static const char * const builtin_clone_usage[] = {
 };
 
 static int option_quiet, option_no_checkout, option_bare, option_mirror;
-static int option_local, option_no_hardlinks, option_shared;
+static int option_local, option_no_hardlinks, option_shared, option_recursive;
 static char *option_template, *option_reference, *option_depth;
 static char *option_origin = NULL;
+static char *option_branch = NULL;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbose;
 
@@ -59,12 +60,16 @@ static struct option builtin_clone_options[] = {
                    "don't use local hardlinks, always copy"),
        OPT_BOOLEAN('s', "shared", &option_shared,
                    "setup as shared repository"),
+       OPT_BOOLEAN(0, "recursive", &option_recursive,
+                   "setup as shared repository"),
        OPT_STRING(0, "template", &option_template, "path",
                   "path the template repository"),
        OPT_STRING(0, "reference", &option_reference, "repo",
                   "reference repository"),
        OPT_STRING('o', "origin", &option_origin, "branch",
                   "use <branch> instead of 'origin' to track upstream"),
+       OPT_STRING('b', "branch", &option_branch, "branch",
+                  "checkout <branch> instead of the remote's HEAD"),
        OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
                   "path to git-upload-pack on the remote"),
        OPT_STRING(0, "depth", &option_depth, "depth",
@@ -73,6 +78,10 @@ static struct option builtin_clone_options[] = {
        OPT_END()
 };
 
+static const char *argv_submodule[] = {
+       "submodule", "update", "--init", "--recursive", NULL
+};
+
 static char *get_repo_path(const char *repo, int *is_bundle)
 {
        static char *suffix[] = { "/.git", ".git", "" };
@@ -104,11 +113,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 +150,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, NULL);
+       } 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)
@@ -196,13 +229,13 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
 
        dir = opendir(src->buf);
        if (!dir)
-               die("failed to open %s", src->buf);
+               die_errno("failed to open '%s'", src->buf);
 
        if (mkdir(dest->buf, 0777)) {
                if (errno != EEXIST)
-                       die("failed to create directory %s", dest->buf);
+                       die_errno("failed to create directory '%s'", dest->buf);
                else if (stat(dest->buf, &buf))
-                       die("failed to stat %s", dest->buf);
+                       die_errno("failed to stat '%s'", dest->buf);
                else if (!S_ISDIR(buf.st_mode))
                        die("%s exists and is not a directory", dest->buf);
        }
@@ -228,16 +261,16 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
                }
 
                if (unlink(dest->buf) && errno != ENOENT)
-                       die("failed to unlink %s", dest->buf);
+                       die_errno("failed to unlink '%s'", dest->buf);
                if (!option_no_hardlinks) {
                        if (!link(src->buf, dest->buf))
                                continue;
                        if (option_local)
-                               die("failed to create link %s", dest->buf);
+                               die_errno("failed to create link '%s'", dest->buf);
                        option_no_hardlinks = 1;
                }
-               if (copy_file(dest->buf, src->buf, 0666))
-                       die("failed to copy file to %s", dest->buf);
+               if (copy_file_with_time(dest->buf, src->buf, 0666))
+                       die_errno("failed to copy file to '%s'", dest->buf);
        }
        closedir(dir);
 }
@@ -296,24 +329,28 @@ static void remove_junk_on_signal(int signo)
        raise(signo);
 }
 
-static struct ref *write_remote_refs(const struct ref *refs,
-               struct refspec *refspec, const char *reflog)
+static struct ref *wanted_peer_refs(const struct ref *refs,
+               struct refspec *refspec)
 {
        struct ref *local_refs = NULL;
        struct ref **tail = &local_refs;
-       struct ref *r;
 
        get_fetch_map(refs, refspec, &tail, 0);
        if (!option_mirror)
                get_fetch_map(refs, tag_refspec, &tail, 0);
 
+       return local_refs;
+}
+
+static void write_remote_refs(const struct ref *local_refs)
+{
+       const struct ref *r;
+
        for (r = local_refs; r; r = r->next)
                add_extra_ref(r->peer_ref->name, r->old_sha1, 0);
 
        pack_refs(PACK_REFS_ALL);
        clear_extra_refs();
-
-       return local_refs;
 }
 
 int cmd_clone(int argc, const char **argv, const char *prefix)
@@ -323,7 +360,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        const char *repo_name, *repo, *work_tree, *git_dir;
        char *path, *dir;
        int dest_exists;
-       const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
+       const struct ref *refs, *remote_head, *mapped_refs;
+       const struct ref *remote_head_points_at;
+       const struct ref *our_head_points_at;
        struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
        struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
        struct transport *transport = NULL;
@@ -335,7 +374,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)
@@ -395,11 +434,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (!option_bare) {
                junk_work_tree = work_tree;
                if (safe_create_leading_directories_const(work_tree) < 0)
-                       die("could not create leading directories of '%s': %s",
-                                       work_tree, strerror(errno));
+                       die_errno("could not create leading directories of '%s'",
+                                 work_tree);
                if (!dest_exists && mkdir(work_tree, 0755))
-                       die("could not create work tree dir '%s': %s.",
-                                       work_tree, strerror(errno));
+                       die_errno("could not create work tree dir '%s'.",
+                                 work_tree);
                set_git_work_tree(work_tree);
        }
        junk_git_dir = git_dir;
@@ -460,9 +499,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        strbuf_reset(&value);
 
-       if (path && !is_bundle)
+       if (path && !is_bundle) {
                refs = clone_local(path, git_dir);
-       else {
+               mapped_refs = wanted_peer_refs(refs, refspec);
+       } else {
                struct remote *remote = remote_get(argv[0]);
                transport = transport_get(remote, remote->url[0]);
 
@@ -485,21 +525,43 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                             option_upload_pack);
 
                refs = transport_get_remote_refs(transport);
-               if(refs)
-                       transport_fetch_refs(transport, refs);
+               if (refs) {
+                       mapped_refs = wanted_peer_refs(refs, refspec);
+                       transport_fetch_refs(transport, mapped_refs);
+               }
        }
 
        if (refs) {
                clear_extra_refs();
 
-               mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
+               write_remote_refs(mapped_refs);
 
                remote_head = find_ref_by_name(refs, "HEAD");
-               head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+               remote_head_points_at =
+                       guess_remote_head(remote_head, mapped_refs, 0);
+
+               if (option_branch) {
+                       struct strbuf head = STRBUF_INIT;
+                       strbuf_addstr(&head, src_ref_prefix);
+                       strbuf_addstr(&head, option_branch);
+                       our_head_points_at =
+                               find_ref_by_name(mapped_refs, head.buf);
+                       strbuf_release(&head);
+
+                       if (!our_head_points_at) {
+                               warning("Remote branch %s not found in "
+                                       "upstream %s, using HEAD instead",
+                                       option_branch, option_origin);
+                               our_head_points_at = remote_head_points_at;
+                       }
+               }
+               else
+                       our_head_points_at = remote_head_points_at;
        }
        else {
                warning("You appear to have cloned an empty repository.");
-               head_points_at = NULL;
+               our_head_points_at = NULL;
+               remote_head_points_at = NULL;
                remote_head = NULL;
                option_no_checkout = 1;
                if (!option_bare)
@@ -507,41 +569,35 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                              "refs/heads/master");
        }
 
-       if (head_points_at) {
-               /* Local default branch link */
-               create_symref("HEAD", head_points_at->name, NULL);
+       if (remote_head_points_at && !option_bare) {
+               struct strbuf head_ref = STRBUF_INIT;
+               strbuf_addstr(&head_ref, branch_top.buf);
+               strbuf_addstr(&head_ref, "HEAD");
+               create_symref(head_ref.buf,
+                             remote_head_points_at->peer_ref->name,
+                             reflog_msg.buf);
+       }
 
+       if (our_head_points_at) {
+               /* Local default branch link */
+               create_symref("HEAD", our_head_points_at->name, NULL);
                if (!option_bare) {
-                       struct strbuf head_ref = STRBUF_INIT;
-                       const char *head = head_points_at->name;
-
-                       if (!prefixcmp(head, "refs/heads/"))
-                               head += 11;
-
-                       /* Set up the initial local branch */
-
-                       /* Local branch initial value */
+                       const char *head = skip_prefix(our_head_points_at->name,
+                                                      "refs/heads/");
                        update_ref(reflog_msg.buf, "HEAD",
-                                  head_points_at->old_sha1,
+                                  our_head_points_at->old_sha1,
                                   NULL, 0, DIE_ON_ERR);
-
-                       strbuf_addstr(&head_ref, branch_top.buf);
-                       strbuf_addstr(&head_ref, "HEAD");
-
-                       /* Remote branch link */
-                       create_symref(head_ref.buf,
-                                     head_points_at->peer_ref->name,
-                                     reflog_msg.buf);
-
                        install_branch_config(0, head, option_origin,
-                                             head_points_at->name);
+                                             our_head_points_at->name);
                }
        } else if (remote_head) {
                /* Source had detached HEAD pointing somewhere. */
-               if (!option_bare)
+               if (!option_bare) {
                        update_ref(reflog_msg.buf, "HEAD",
                                   remote_head->old_sha1,
                                   NULL, REF_NODEREF, DIE_ON_ERR);
+                       our_head_points_at = remote_head;
+               }
        } else {
                /* Nothing to checkout out */
                if (!option_no_checkout)
@@ -550,8 +606,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                option_no_checkout = 1;
        }
 
-       if (transport)
+       if (transport) {
                transport_unlock_pack(transport);
+               transport_disconnect(transport);
+       }
 
        if (!option_no_checkout) {
                struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
@@ -573,7 +631,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                opts.src_index = &the_index;
                opts.dst_index = &the_index;
 
-               tree = parse_tree_indirect(remote_head->old_sha1);
+               tree = parse_tree_indirect(our_head_points_at->old_sha1);
                parse_tree(tree);
                init_tree_desc(&t, tree->buffer, tree->size);
                unpack_trees(1, &t, &opts);
@@ -583,7 +641,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        die("unable to write new index file");
 
                err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
-                               sha1_to_hex(remote_head->old_sha1), "1", NULL);
+                               sha1_to_hex(our_head_points_at->old_sha1), "1",
+                               NULL);
+
+               if (!err && option_recursive)
+                       err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
        }
 
        strbuf_release(&reflog_msg);
index 0453425c471f1d6793bc7f5f59d17e9d285ddfc6..64670777312c71d45b10f72a1efe9fceae3e3b89 100644 (file)
@@ -124,7 +124,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        }
 
        if (strbuf_read(&buffer, 0, 0) < 0)
-               die("git commit-tree: read returned %s", strerror(errno));
+               die_errno("git commit-tree: failed to read");
 
        if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
                printf("%s\n", sha1_to_hex(commit_sha1));
index 81371b1d2698a48dba36046d7ff9d849f830a762..200ffdaad4226ae2021c86ae4b7a2f8ccefb3600 100644 (file)
@@ -51,7 +51,7 @@ static const char *template_file;
 static char *edit_message, *use_message;
 static char *author_name, *author_email, *author_date;
 static int all, edit_flag, also, interactive, only, amend, signoff;
-static int quiet, verbose, no_verify, allow_empty;
+static int quiet, verbose, no_verify, allow_empty, dry_run;
 static char *untracked_files_arg;
 /*
  * The default commit message cleanup mode will remove the lines
@@ -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"),
@@ -103,6 +103,7 @@ static struct option builtin_commit_options[] = {
        OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
        OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
        OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+       OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
        OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
@@ -217,12 +218,15 @@ static void create_base_index(void)
                exit(128); /* We've already reported the error, finish dying */
 }
 
-static char *prepare_index(int argc, const char **argv, const char *prefix)
+static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status)
 {
        int fd;
        struct string_list partial;
        const char **pathspec = NULL;
+       int refresh_flags = REFRESH_QUIET;
 
+       if (is_status)
+               refresh_flags |= REFRESH_UNMERGED;
        if (interactive) {
                if (interactive_add(argc, argv, prefix) != 0)
                        die("interactive add failed");
@@ -253,7 +257,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
        if (all || (also && pathspec && *pathspec)) {
                int fd = hold_locked_index(&index_lock, 1);
                add_files_to_cache(also ? prefix : NULL, pathspec, 0);
-               refresh_cache(REFRESH_QUIET);
+               refresh_cache(refresh_flags);
                if (write_cache(fd, active_cache, active_nr) ||
                    close_lock_file(&index_lock))
                        die("unable to write new_index file");
@@ -272,7 +276,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
         */
        if (!pathspec || !*pathspec) {
                fd = hold_locked_index(&index_lock, 1);
-               refresh_cache(REFRESH_QUIET);
+               refresh_cache(refresh_flags);
                if (write_cache(fd, active_cache, active_nr) ||
                    commit_locked_index(&index_lock))
                        die("unable to write new_index file");
@@ -339,27 +343,24 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
        return false_lock.filename;
 }
 
-static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn)
+static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
+                     struct wt_status *s)
 {
-       struct wt_status s;
-
-       wt_status_prepare(&s);
-       if (wt_status_relative_paths)
-               s.prefix = prefix;
+       if (s->relative_paths)
+               s->prefix = prefix;
 
        if (amend) {
-               s.amend = 1;
-               s.reference = "HEAD^1";
+               s->amend = 1;
+               s->reference = "HEAD^1";
        }
-       s.verbose = verbose;
-       s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES);
-       s.index_file = index_file;
-       s.fp = fp;
-       s.nowarn = nowarn;
+       s->verbose = verbose;
+       s->index_file = index_file;
+       s->fp = fp;
+       s->nowarn = nowarn;
 
-       wt_status_print(&s);
+       wt_status_print(s);
 
-       return s.commitable;
+       return s->commitable;
 }
 
 static int is_a_merge(const unsigned char *sha1)
@@ -413,7 +414,8 @@ static void determine_author_info(void)
        author_date = date;
 }
 
-static int prepare_to_commit(const char *index_file, const char *prefix)
+static int prepare_to_commit(const char *index_file, const char *prefix,
+                            struct wt_status *s)
 {
        struct stat statbuf;
        int commitable, saved_color_setting;
@@ -434,12 +436,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
                if (isatty(0))
                        fprintf(stderr, "(reading log message from standard input)\n");
                if (strbuf_read(&sb, 0, 0) < 0)
-                       die("could not read log from standard input");
+                       die_errno("could not read log from standard input");
                hook_arg1 = "message";
        } else if (logfile) {
                if (strbuf_read_file(&sb, logfile, 0) < 0)
-                       die("could not read log file '%s': %s",
-                           logfile, strerror(errno));
+                       die_errno("could not read log file '%s'",
+                                 logfile);
                hook_arg1 = "message";
        } else if (use_message) {
                buffer = strstr(use_message_buffer, "\n\n");
@@ -450,16 +452,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
                hook_arg2 = use_message;
        } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
-                       die("could not read MERGE_MSG: %s", strerror(errno));
+                       die_errno("could not read MERGE_MSG");
                hook_arg1 = "merge";
        } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
-                       die("could not read SQUASH_MSG: %s", strerror(errno));
+                       die_errno("could not read SQUASH_MSG");
                hook_arg1 = "squash";
        } else if (template_file && !stat(template_file, &statbuf)) {
                if (strbuf_read_file(&sb, template_file, 0) < 0)
-                       die("could not read %s: %s",
-                           template_file, strerror(errno));
+                       die_errno("could not read '%s'", template_file);
                hook_arg1 = "template";
        }
 
@@ -472,8 +473,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
 
        fp = fopen(git_path(commit_editmsg), "w");
        if (fp == NULL)
-               die("could not open %s: %s",
-                   git_path(commit_editmsg), strerror(errno));
+               die_errno("could not open '%s'", git_path(commit_editmsg));
 
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, 0);
@@ -497,7 +497,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
        }
 
        if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
-               die("could not write commit template: %s", strerror(errno));
+               die_errno("could not write commit template");
 
        strbuf_release(&sb);
 
@@ -557,10 +557,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
                if (ident_shown)
                        fprintf(fp, "#\n");
 
-               saved_color_setting = wt_status_use_color;
-               wt_status_use_color = 0;
-               commitable = run_status(fp, index_file, prefix, 1);
-               wt_status_use_color = saved_color_setting;
+               saved_color_setting = s->use_color;
+               s->use_color = 0;
+               commitable = run_status(fp, index_file, prefix, 1, s);
+               s->use_color = saved_color_setting;
        } else {
                unsigned char sha1[20];
                const char *parent = "HEAD";
@@ -581,7 +581,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
 
        if (!commitable && !in_merge && !allow_empty &&
            !(amend && is_a_merge(head_sha1))) {
-               run_status(stdout, index_file, prefix, 0);
+               run_status(stdout, index_file, prefix, 0, s);
                return 0;
        }
 
@@ -693,13 +693,13 @@ static const char *find_author_by_nickname(const char *name)
 
 static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[],
-                                     const char *prefix)
+                                     const char *prefix,
+                                     struct wt_status *s)
 {
        int f = 0;
 
-       argc = parse_options(argc, argv, builtin_commit_options, usage, 0);
-       logfile = parse_options_fix_filename(prefix, logfile);
-       template_file = parse_options_fix_filename(prefix, 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);
@@ -797,11 +797,11 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (!untracked_files_arg)
                ; /* default already initialized */
        else if (!strcmp(untracked_files_arg, "no"))
-               show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+               s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
        else if (!strcmp(untracked_files_arg, "normal"))
-               show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+               s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
        else if (!strcmp(untracked_files_arg, "all"))
-               show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+               s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
        else
                die("Invalid untracked files mode '%s'", untracked_files_arg);
 
@@ -813,28 +813,93 @@ static int parse_and_validate_options(int argc, const char *argv[],
        return argc;
 }
 
-int cmd_status(int argc, const char **argv, const char *prefix)
+static int dry_run_commit(int argc, const char **argv, const char *prefix,
+                         struct wt_status *s)
 {
-       const char *index_file;
        int commitable;
+       const char *index_file;
 
-       git_config(git_status_config, NULL);
+       index_file = prepare_index(argc, argv, prefix, 1);
+       commitable = run_status(stdout, index_file, prefix, 0, s);
+       rollback_index_files();
 
-       if (wt_status_use_color == -1)
-               wt_status_use_color = git_use_color_default;
+       return commitable ? 0 : 1;
+}
 
-       if (diff_use_color_default == -1)
-               diff_use_color_default = git_use_color_default;
+static int parse_status_slot(const char *var, int offset)
+{
+       if (!strcasecmp(var+offset, "header"))
+               return WT_STATUS_HEADER;
+       if (!strcasecmp(var+offset, "updated")
+               || !strcasecmp(var+offset, "added"))
+               return WT_STATUS_UPDATED;
+       if (!strcasecmp(var+offset, "changed"))
+               return WT_STATUS_CHANGED;
+       if (!strcasecmp(var+offset, "untracked"))
+               return WT_STATUS_UNTRACKED;
+       if (!strcasecmp(var+offset, "nobranch"))
+               return WT_STATUS_NOBRANCH;
+       if (!strcasecmp(var+offset, "unmerged"))
+               return WT_STATUS_UNMERGED;
+       die("bad config variable '%s'", var);
+}
 
-       argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix);
+static int git_status_config(const char *k, const char *v, void *cb)
+{
+       struct wt_status *s = cb;
 
-       index_file = prepare_index(argc, argv, prefix);
+       if (!strcmp(k, "status.submodulesummary")) {
+               int is_bool;
+               s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+               if (is_bool && s->submodule_summary)
+                       s->submodule_summary = -1;
+               return 0;
+       }
+       if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
+               s->use_color = git_config_colorbool(k, v, -1);
+               return 0;
+       }
+       if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+               int slot = parse_status_slot(k, 13);
+               if (!v)
+                       return config_error_nonbool(k);
+               color_parse(v, k, s->color_palette[slot]);
+               return 0;
+       }
+       if (!strcmp(k, "status.relativepaths")) {
+               s->relative_paths = git_config_bool(k, v);
+               return 0;
+       }
+       if (!strcmp(k, "status.showuntrackedfiles")) {
+               if (!v)
+                       return config_error_nonbool(k);
+               else if (!strcmp(v, "no"))
+                       s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+               else if (!strcmp(v, "normal"))
+                       s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+               else if (!strcmp(v, "all"))
+                       s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+               else
+                       return error("Invalid untracked files mode '%s'", v);
+               return 0;
+       }
+       return git_diff_ui_config(k, v, NULL);
+}
 
-       commitable = run_status(stdout, index_file, prefix, 0);
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+       struct wt_status s;
 
-       rollback_index_files();
+       wt_status_prepare(&s);
+       git_config(git_status_config, &s);
+       if (s.use_color == -1)
+               s.use_color = git_use_color_default;
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
 
-       return commitable ? 0 : 1;
+       argc = parse_and_validate_options(argc, argv, builtin_status_usage,
+                                         prefix, &s);
+       return dry_run_commit(argc, argv, prefix, &s);
 }
 
 static void print_summary(const char *prefix, const unsigned char *sha1)
@@ -886,10 +951,12 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
 
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
+       struct wt_status *s = cb;
+
        if (!strcmp(k, "commit.template"))
                return git_config_string(&template_file, k, v);
 
-       return git_status_config(k, v, cb);
+       return git_status_config(k, v, s);
 }
 
 int cmd_commit(int argc, const char **argv, const char *prefix)
@@ -902,19 +969,26 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct commit_list *parents = NULL, **pptr = &parents;
        struct stat statbuf;
        int allow_fast_forward = 1;
+       struct wt_status s;
 
-       git_config(git_commit_config, NULL);
-
-       if (wt_status_use_color == -1)
-               wt_status_use_color = git_use_color_default;
+       wt_status_prepare(&s);
+       git_config(git_commit_config, &s);
 
-       argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix);
+       if (s.use_color == -1)
+               s.use_color = git_use_color_default;
 
-       index_file = prepare_index(argc, argv, prefix);
+       argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
+                                         prefix, &s);
+       if (dry_run) {
+               if (diff_use_color_default == -1)
+                       diff_use_color_default = git_use_color_default;
+               return dry_run_commit(argc, argv, prefix, &s);
+       }
+       index_file = prepare_index(argc, argv, prefix, 0);
 
        /* Set up everything for writing the commit object.  This includes
           running hooks, writing the trees, and interacting with the user.  */
-       if (!prepare_to_commit(index_file, prefix)) {
+       if (!prepare_to_commit(index_file, prefix, &s)) {
                rollback_index_files();
                return 1;
        }
@@ -941,8 +1015,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
                fp = fopen(git_path("MERGE_HEAD"), "r");
                if (fp == NULL)
-                       die("could not open %s for reading: %s",
-                           git_path("MERGE_HEAD"), strerror(errno));
+                       die_errno("could not open '%s' for reading",
+                                 git_path("MERGE_HEAD"));
                while (strbuf_getline(&m, fp, '\n') != EOF) {
                        unsigned char sha1[20];
                        if (get_sha1_hex(m.buf, sha1) < 0)
@@ -953,8 +1027,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                strbuf_release(&m);
                if (!stat(git_path("MERGE_MODE"), &statbuf)) {
                        if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
-                               die("could not read MERGE_MODE: %s",
-                                               strerror(errno));
+                               die_errno("could not read MERGE_MODE");
                        if (!strcmp(sb.buf, "no-ff"))
                                allow_fast_forward = 0;
                }
@@ -968,8 +1041,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        /* Finally, get the commit message */
        strbuf_reset(&sb);
        if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+               int saved_errno = errno;
                rollback_index_files();
-               die("could not read commit message");
+               die("could not read commit message: %s", strerror(saved_errno));
        }
 
        /* Truncate the message just before the diff, if any. */
index a81bc8bbf033fac83ad6deecbf4a67ba44e06a0b..a2d656edb383da47fb3622f1f7c2d1524285ac00 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) {
@@ -382,8 +383,8 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
                check_argc(argc, 0, 0);
                if (git_config(show_all_config, NULL) < 0) {
                        if (config_exclusive_filename)
-                               die("unable to read config file %s: %s",
-                                   config_exclusive_filename, strerror(errno));
+                               die_errno("unable to read config file '%s'",
+                                         config_exclusive_filename);
                        else
                                die("error processing config file(s)");
                }
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..2dcfd3dfebdaad8fe87b8d09fa00b058f519ac5a 100644 (file)
@@ -20,6 +20,7 @@ static int tags;      /* Allow lightweight tags */
 static int longformat;
 static int abbrev = DEFAULT_ABBREV;
 static int max_candidates = 10;
+static int found_names;
 static const char *pattern;
 static int always;
 
@@ -49,6 +50,7 @@ static void add_to_known_names(const char *path,
                memcpy(e->path, path, len);
                commit->util = e;
        }
+       found_names = 1;
 }
 
 static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
@@ -178,7 +180,6 @@ static void describe(const char *arg, int last_one)
        unsigned char sha1[20];
        struct commit *cmit, *gave_up_on = NULL;
        struct commit_list *list;
-       static int initialized = 0;
        struct commit_name *n;
        struct possible_tag all_matches[MAX_TAGS];
        unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
@@ -190,11 +191,6 @@ static void describe(const char *arg, int last_one)
        if (!cmit)
                die("%s is not a valid '%s' object", arg, commit_type);
 
-       if (!initialized) {
-               initialized = 1;
-               for_each_ref(get_name, NULL);
-       }
-
        n = cmit->util;
        if (n) {
                /*
@@ -322,7 +318,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)
@@ -354,6 +350,10 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                return cmd_name_rev(i + argc, args, prefix);
        }
 
+       for_each_ref(get_name, NULL);
+       if (!found_names)
+               die("No names found, cannot describe anything.");
+
        if (argc == 0) {
                describe("HEAD", 1);
        } else {
index d75d69bf5774ffd402bbeec47b9a0e0800554d63..ffcdd055ca0b9b30bec2ce1f22e348ec15d58c81 100644 (file)
@@ -70,7 +70,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
                usage(builtin_diff_usage);
 
        if (lstat(path, &st))
-               die("'%s': %s", path, strerror(errno));
+               die_errno("failed to stat '%s'", path);
        if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
                die("'%s': not a regular file or symlink", path);
 
@@ -218,6 +218,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
                        revs->max_count = 3;
                else if (!strcmp(argv[1], "-q"))
                        options |= DIFF_SILENT_ON_REMOVED;
+               else if (!strcmp(argv[1], "-h"))
+                       usage(builtin_diff_usage);
                else
                        return error("invalid option: %s", argv[1]);
                argv++; argc--;
index 6731713223d4df24614417cc4285cbee793151d3..b0a4029c94d1bdb1c673fe604cdbfec93df875aa 100644 (file)
@@ -23,8 +23,10 @@ static const char *fast_export_usage[] = {
 };
 
 static int progress;
-static enum { VERBATIM, WARN, STRIP, ABORT } signed_tag_mode = ABORT;
+static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
+static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
 static int fake_missing_tagger;
+static int no_data;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
@@ -42,6 +44,20 @@ static int parse_opt_signed_tag_mode(const struct option *opt,
        return 0;
 }
 
+static int parse_opt_tag_of_filtered_mode(const struct option *opt,
+                                         const char *arg, int unset)
+{
+       if (unset || !strcmp(arg, "abort"))
+               tag_of_filtered_mode = ABORT;
+       else if (!strcmp(arg, "drop"))
+               tag_of_filtered_mode = DROP;
+       else if (!strcmp(arg, "rewrite"))
+               tag_of_filtered_mode = REWRITE;
+       else
+               return error("Unknown tag-of-filtered mode: %s", arg);
+       return 0;
+}
+
 static struct decoration idnums;
 static uint32_t last_idnum;
 
@@ -101,6 +117,9 @@ static void handle_object(const unsigned char *sha1)
        char *buf;
        struct object *object;
 
+       if (no_data)
+               return;
+
        if (is_null_sha1(sha1))
                return;
 
@@ -119,7 +138,7 @@ static void handle_object(const unsigned char *sha1)
 
        printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size);
        if (size && fwrite(buf, size, 1, stdout) != 1)
-               die ("Could not write blob %s", sha1_to_hex(sha1));
+               die_errno ("Could not write blob '%s'", sha1_to_hex(sha1));
        printf("\n");
 
        show_progress();
@@ -158,7 +177,7 @@ static void show_filemodify(struct diff_queue_struct *q,
                         * Links refer to objects in another repositories;
                         * output the SHA-1 verbatim.
                         */
-                       if (S_ISGITLINK(spec->mode))
+                       if (no_data || S_ISGITLINK(spec->mode))
                                printf("M %06o %s %s\n", spec->mode,
                                       sha1_to_hex(spec->sha1), spec->path);
                        else {
@@ -289,6 +308,23 @@ static void handle_tag(const char *name, struct tag *tag)
        char *buf;
        const char *tagger, *tagger_end, *message;
        size_t message_size = 0;
+       struct object *tagged;
+       int tagged_mark;
+       struct commit *p;
+
+       /* Trees have no identifer in fast-export output, thus we have no way
+        * to output tags of trees, tags of tags of trees, etc.  Simply omit
+        * such tags.
+        */
+       tagged = tag->tagged;
+       while (tagged->type == OBJ_TAG) {
+               tagged = ((struct tag *)tagged)->tagged;
+       }
+       if (tagged->type == OBJ_TREE) {
+               warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
+                       sha1_to_hex(tag->object.sha1));
+               return;
+       }
 
        buf = read_sha1_file(tag->object.sha1, &type, &size);
        if (!buf)
@@ -333,10 +369,45 @@ static void handle_tag(const char *name, struct tag *tag)
                        }
        }
 
+       /* handle tag->tagged having been filtered out due to paths specified */
+       tagged = tag->tagged;
+       tagged_mark = get_object_mark(tagged);
+       if (!tagged_mark) {
+               switch(tag_of_filtered_mode) {
+               case ABORT:
+                       die ("Tag %s tags unexported object; use "
+                            "--tag-of-filtered-object=<mode> to handle it.",
+                            sha1_to_hex(tag->object.sha1));
+               case DROP:
+                       /* Ignore this tag altogether */
+                       return;
+               case REWRITE:
+                       if (tagged->type != OBJ_COMMIT) {
+                               die ("Tag %s tags unexported %s!",
+                                    sha1_to_hex(tag->object.sha1),
+                                    typename(tagged->type));
+                       }
+                       p = (struct commit *)tagged;
+                       for (;;) {
+                               if (p->parents && p->parents->next)
+                                       break;
+                               if (p->object.flags & UNINTERESTING)
+                                       break;
+                               if (!(p->object.flags & TREESAME))
+                                       break;
+                               if (!p->parents)
+                                       die ("Can't find replacement commit for tag %s\n",
+                                            sha1_to_hex(tag->object.sha1));
+                               p = p->parents->item;
+                       }
+                       tagged_mark = get_object_mark(&p->object);
+               }
+       }
+
        if (!prefixcmp(name, "refs/tags/"))
                name += 10;
        printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
-              name, get_object_mark(tag->tagged),
+              name, tagged_mark,
               (int)(tagger_end - tagger), tagger,
               tagger == tagger_end ? "" : "\n",
               (int)message_size, (int)message_size, message ? message : "");
@@ -428,21 +499,27 @@ static void export_marks(char *file)
        uint32_t mark;
        struct object_decoration *deco = idnums.hash;
        FILE *f;
+       int e = 0;
 
        f = fopen(file, "w");
        if (!f)
-               error("Unable to open marks file %s for writing", file);
+               error("Unable to open marks file %s for writing.", file);
 
        for (i = 0; i < idnums.size; i++) {
                if (deco->base && deco->base->type == 1) {
                        mark = ptr_to_mark(deco->decoration);
-                       fprintf(f, ":%"PRIu32" %s\n", mark,
-                               sha1_to_hex(deco->base->sha1));
+                       if (fprintf(f, ":%"PRIu32" %s\n", mark,
+                               sha1_to_hex(deco->base->sha1)) < 0) {
+                           e = 1;
+                           break;
+                       }
                }
                deco++;
        }
 
-       if (ferror(f) || fclose(f))
+       e |= ferror(f);
+       e |= fclose(f);
+       if (e)
                error("Unable to write marks file %s.", file);
 }
 
@@ -451,7 +528,7 @@ static void import_marks(char *input_file)
        char line[512];
        FILE *f = fopen(input_file, "r");
        if (!f)
-               die("cannot read %s: %s", input_file, strerror(errno));
+               die_errno("cannot read '%s'", input_file);
 
        while (fgets(line, sizeof(line), f)) {
                uint32_t mark;
@@ -498,12 +575,18 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
                             "select handling of signed tags",
                             parse_opt_signed_tag_mode),
+               OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, "mode",
+                            "select handling of tags that tag filtered objects",
+                            parse_opt_tag_of_filtered_mode),
                OPT_STRING(0, "export-marks", &export_filename, "FILE",
                             "Dump marks to this file"),
                OPT_STRING(0, "import-marks", &import_filename, "FILE",
                             "Import marks from this file"),
                OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
                             "Fake a tagger when tags lack one"),
+               { OPTION_NEGBIT, 0, "data", &no_data, NULL,
+                       "Skip output of blob data",
+                       PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
                OPT_END()
        };
 
@@ -514,8 +597,11 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        git_config(git_default_config, NULL);
 
        init_revisions(&revs, prefix);
+       revs.topo_order = 1;
+       revs.show_source = 1;
+       revs.rewrite_parents = 1;
        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);
 
@@ -524,18 +610,13 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 
        get_tags_and_duplicates(&revs.pending, &extra_refs);
 
-       revs.topo_order = 1;
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        revs.diffopt.format_callback = show_filemodify;
        DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
        while ((commit = get_revision(&revs))) {
                if (has_unshown_parent(commit)) {
-                       struct commit_list *parent = commit->parents;
                        add_object_array(&commit->object, NULL, &commits);
-                       for (; parent; parent = parent->next)
-                               if (!parent->item->util)
-                                       parent->item->util = commit->util;
                }
                else {
                        handle_commit(commit, &revs);
index 29356d25db910c6d90df46da87aa374467611350..3dbdf7a2887002f0d7a67a1db35bd3c72d7a9d30 100644 (file)
@@ -8,7 +8,7 @@ static char *get_stdin(void)
 {
        struct strbuf buf = STRBUF_INIT;
        if (strbuf_read(&buf, 0, 1024) < 0) {
-               die("error reading standard input: %s", strerror(errno));
+               die_errno("error reading standard input");
        }
        return strbuf_detach(&buf, NULL);
 }
index 87f46c6d07ccb0f82449b92e33cd4cda76a99da3..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)
@@ -825,7 +827,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                fd = hold_lock_file_for_update(&lock, shallow,
                                               LOCK_DIE_ON_ERROR);
                if (!write_shallow_commits(fd, 0)) {
-                       unlink(shallow);
+                       unlink_or_warn(shallow);
                        rollback_lock_file(&lock);
                } else {
                        commit_lock_file(&lock);
index 3c998ea740adf474ee980fc763bd7156b9ce81af..a35a6f8cb8030bf9be98a001da26fddf0efdb912 100644 (file)
@@ -167,6 +167,9 @@ static struct ref *get_ref_map(struct transport *transport,
        return ref_map;
 }
 
+#define STORE_REF_ERROR_OTHER 1
+#define STORE_REF_ERROR_DF_CONFLICT 2
+
 static int s_update_ref(const char *action,
                        struct ref *ref,
                        int check_old)
@@ -181,9 +184,11 @@ static int s_update_ref(const char *action,
        lock = lock_any_ref_for_update(ref->name,
                                       check_old ? ref->old_sha1 : NULL, 0);
        if (!lock)
-               return 2;
+               return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
+                                         STORE_REF_ERROR_OTHER;
        if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
-               return 2;
+               return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
+                                         STORE_REF_ERROR_OTHER;
        return 0;
 }
 
@@ -197,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);
@@ -289,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;
@@ -298,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;
 
@@ -353,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);
@@ -376,8 +389,9 @@ static int store_updated_refs(const char *url, const char *remote_name,
                                fprintf(stderr, " %s\n", note);
                }
        }
+       free(url);
        fclose(fp);
-       if (rc & 2)
+       if (rc & STORE_REF_ERROR_DF_CONFLICT)
                error("some local refs could not be updated; try running\n"
                      " 'git remote prune %s' to remove any old, conflicting "
                      "branches", remote_name);
@@ -386,14 +400,14 @@ static int store_updated_refs(const char *url, const char *remote_name,
 
 /*
  * We would want to bypass the object transfer altogether if
- * everything we are going to fetch already exists and connected
+ * everything we are going to fetch already exists and is connected
  * locally.
  *
- * The refs we are going to fetch are in to_fetch (nr_heads in
- * total).  If running
+ * The refs we are going to fetch are in ref_map.  If running
  *
- *  $ git rev-list --objects to_fetch[0] to_fetch[1] ... --not --all
+ *  $ git rev-list --objects --stdin --not --all
  *
+ * (feeding all the refs in ref_map on its standard input)
  * does not error out, that means everything reachable from the
  * refs we are going to fetch exists and is connected to some of
  * our existing refs.
@@ -402,8 +416,9 @@ static int quickfetch(struct ref *ref_map)
 {
        struct child_process revlist;
        struct ref *ref;
-       char **argv;
-       int i, err;
+       int err;
+       const char *argv[] = {"rev-list",
+               "--quiet", "--objects", "--stdin", "--not", "--all", NULL};
 
        /*
         * If we are deepening a shallow clone we already have these
@@ -415,34 +430,46 @@ static int quickfetch(struct ref *ref_map)
        if (depth)
                return -1;
 
-       for (i = 0, ref = ref_map; ref; ref = ref->next)
-               i++;
-       if (!i)
+       if (!ref_map)
                return 0;
 
-       argv = xmalloc(sizeof(*argv) * (i + 6));
-       i = 0;
-       argv[i++] = xstrdup("rev-list");
-       argv[i++] = xstrdup("--quiet");
-       argv[i++] = xstrdup("--objects");
-       for (ref = ref_map; ref; ref = ref->next)
-               argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1));
-       argv[i++] = xstrdup("--not");
-       argv[i++] = xstrdup("--all");
-       argv[i++] = NULL;
-
        memset(&revlist, 0, sizeof(revlist));
-       revlist.argv = (const char**)argv;
+       revlist.argv = argv;
        revlist.git_cmd = 1;
-       revlist.no_stdin = 1;
        revlist.no_stdout = 1;
        revlist.no_stderr = 1;
-       err = run_command(&revlist);
+       revlist.in = -1;
 
-       for (i = 0; argv[i]; i++)
-               free(argv[i]);
-       free(argv);
-       return err;
+       err = start_command(&revlist);
+       if (err) {
+               error("could not run rev-list");
+               return err;
+       }
+
+       /*
+        * If rev-list --stdin encounters an unknown commit, it terminates,
+        * which will cause SIGPIPE in the write loop below.
+        */
+       sigchain_push(SIGPIPE, SIG_IGN);
+
+       for (ref = ref_map; ref; ref = ref->next) {
+               if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
+                   write_str_in_full(revlist.in, "\n") < 0) {
+                       if (errno != EPIPE && errno != EINVAL)
+                               error("failed write to rev-list: %s", strerror(errno));
+                       err = -1;
+                       break;
+               }
+       }
+
+       if (close(revlist.in)) {
+               error("failed to close rev-list's stdin: %s", strerror(errno));
+               err = -1;
+       }
+
+       sigchain_pop(SIGPIPE);
+
+       return finish_command(&revlist) || err;
 }
 
 static int fetch_refs(struct transport *transport, struct ref *ref_map)
@@ -477,57 +504,98 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
        return 0;
 }
 
+struct tag_data {
+       struct ref **head;
+       struct ref ***tail;
+};
+
+static int add_to_tail(struct string_list_item *item, void *cb_data)
+{
+       struct tag_data *data = (struct tag_data *)cb_data;
+       struct ref *rm = NULL;
+
+       /* We have already decided to ignore this item */
+       if (!item->util)
+               return 0;
+
+       rm = alloc_ref(item->string);
+       rm->peer_ref = alloc_ref(item->string);
+       hashcpy(rm->old_sha1, item->util);
+
+       **data->tail = rm;
+       *data->tail = &rm->next;
+
+       return 0;
+}
+
 static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
                        struct ref ***tail)
 {
        struct string_list existing_refs = { NULL, 0, 0, 0 };
-       struct string_list new_refs = { NULL, 0, 0, 1 };
-       char *ref_name;
-       int ref_name_len;
-       const unsigned char *ref_sha1;
-       const struct ref *tag_ref;
-       struct ref *rm = NULL;
+       struct string_list remote_refs = { NULL, 0, 0, 0 };
+       struct tag_data data = {head, tail};
        const struct ref *ref;
+       struct string_list_item *item = NULL;
 
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
                if (prefixcmp(ref->name, "refs/tags"))
                        continue;
 
-               ref_name = xstrdup(ref->name);
-               ref_name_len = strlen(ref_name);
-               ref_sha1 = ref->old_sha1;
-
-               if (!strcmp(ref_name + ref_name_len - 3, "^{}")) {
-                       ref_name[ref_name_len - 3] = 0;
-                       tag_ref = transport_get_remote_refs(transport);
-                       while (tag_ref) {
-                               if (!strcmp(tag_ref->name, ref_name)) {
-                                       ref_sha1 = tag_ref->old_sha1;
-                                       break;
-                               }
-                               tag_ref = tag_ref->next;
-                       }
+               /*
+                * The peeled ref always follows the matching base
+                * ref, so if we see a peeled ref that we don't want
+                * to fetch then we can mark the ref entry in the list
+                * as one to ignore by setting util to NULL.
+                */
+               if (!strcmp(ref->name + strlen(ref->name) - 3, "^{}")) {
+                       if (item && !has_sha1_file(ref->old_sha1) &&
+                           !will_fetch(head, ref->old_sha1) &&
+                           !has_sha1_file(item->util) &&
+                           !will_fetch(head, item->util))
+                               item->util = NULL;
+                       item = NULL;
+                       continue;
                }
 
-               if (!string_list_has_string(&existing_refs, ref_name) &&
-                   !string_list_has_string(&new_refs, ref_name) &&
-                   (has_sha1_file(ref->old_sha1) ||
-                    will_fetch(head, ref->old_sha1))) {
-                       string_list_insert(ref_name, &new_refs);
+               /*
+                * If item is non-NULL here, then we previously saw a
+                * ref not followed by a peeled reference, so we need
+                * to check if it is a lightweight tag that we want to
+                * fetch.
+                */
+               if (item && !has_sha1_file(item->util) &&
+                   !will_fetch(head, item->util))
+                       item->util = NULL;
 
-                       rm = alloc_ref(ref_name);
-                       rm->peer_ref = alloc_ref(ref_name);
-                       hashcpy(rm->old_sha1, ref_sha1);
+               item = NULL;
 
-                       **tail = rm;
-                       *tail = &rm->next;
-               }
-               free(ref_name);
+               /* skip duplicates and refs that we already have */
+               if (string_list_has_string(&remote_refs, ref->name) ||
+                   string_list_has_string(&existing_refs, ref->name))
+                       continue;
+
+               item = string_list_insert(ref->name, &remote_refs);
+               item->util = (void *)ref->old_sha1;
        }
        string_list_clear(&existing_refs, 0);
-       string_list_clear(&new_refs, 0);
+
+       /*
+        * We may have a final lightweight tag that needs to be
+        * checked to see if it needs fetching.
+        */
+       if (item && !has_sha1_file(item->util) &&
+           !will_fetch(head, item->util))
+               item->util = NULL;
+
+       /*
+        * For all the tags in the remote_refs string list, call
+        * add_to_tail to add them to the list of refs to be fetched
+        */
+       for_each_string_list(add_to_tail, &remote_refs, &data);
+
+       string_list_clear(&remote_refs, 0);
 }
 
 static void check_not_current_branch(struct ref *ref_map)
@@ -625,7 +693,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 a7883690d74cb1bcef7b20f27ebbea6f6c5dcc53..9d524000b5ba4d9c7566edd5756b68d728ec362b 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,19 +360,19 @@ 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);
 
        if (inpath && strcmp(inpath, "-")) {
                in = fopen(inpath, "r");
                if (!in)
-                       die("cannot open %s", inpath);
+                       die_errno("cannot open '%s'", inpath);
        }
 
        if (strbuf_read(&input, fileno(in), 0) < 0)
-               die("could not read input file %s", strerror(errno));
-
+               die_errno("could not read input file");
        ret = fmt_merge_msg(merge_summary, &input, &output);
        if (ret)
                return ret;
index 91e8f95fd20215cae72e206a691370c71dbc575e..a5a83f14693b94adf3ae0dbc1b500b2e6b2be54d 100644 (file)
@@ -339,8 +339,11 @@ static const char *copy_name(const char *buf)
 static const char *copy_email(const char *buf)
 {
        const char *email = strchr(buf, '<');
-       const char *eoemail = strchr(email, '>');
-       if (!email || !eoemail)
+       const char *eoemail;
+       if (!email)
+               return "";
+       eoemail = strchr(email, '>');
+       if (!eoemail)
                return "";
        return xmemdupz(email, eoemail + 1 - email);
 }
@@ -558,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];
@@ -581,7 +576,7 @@ static void populate_value(struct refinfo *ref)
 
                if (!prefixcmp(name, "refname"))
                        refname = ref->refname;
-               else if(!prefixcmp(name, "upstream")) {
+               else if (!prefixcmp(name, "upstream")) {
                        struct branch *branch;
                        /* only local branches may have an upstream */
                        if (prefixcmp(ref->refname, "refs/heads/"))
@@ -618,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);
@@ -902,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);
@@ -923,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..c58b0e337ed434ffb90cfb5c80540531d94a4a6a 100644 (file)
@@ -104,7 +104,7 @@ static int mark_object(struct object *obj, int type, void *data)
 
 static void mark_object_reachable(struct object *obj)
 {
-       mark_object(obj, OBJ_ANY, 0);
+       mark_object(obj, OBJ_ANY, NULL);
 }
 
 static int traverse_one_object(struct object *obj, struct object *parent)
@@ -217,7 +217,7 @@ static void check_unreachable_object(struct object *obj)
                                return;
                        }
                        if (!(f = fopen(filename, "w")))
-                               die("Could not open %s", filename);
+                               die_errno("Could not open '%s'", filename);
                        if (obj->type == OBJ_BLOB) {
                                enum object_type type;
                                unsigned long size;
@@ -225,15 +225,15 @@ static void check_unreachable_object(struct object *obj)
                                                &type, &size);
                                if (buf) {
                                        if (fwrite(buf, size, 1, f) != 1)
-                                               die("Could not write %s: %s",
-                                                   filename, strerror(errno));
+                                               die_errno("Could not write '%s'",
+                                                         filename);
                                        free(buf);
                                }
                        } else
                                fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
                        if (fclose(f))
-                               die("Could not finish %s: %s",
-                                   filename, strerror(errno));
+                               die_errno("Could not finish '%s'",
+                                         filename);
                }
                return;
        }
@@ -292,7 +292,7 @@ static int fsck_sha1(const unsigned char *sha1)
                fprintf(stderr, "Checking %s %s\n",
                        typename(obj->type), sha1_to_hex(obj->sha1));
 
-       if (fsck_walk(obj, mark_used, 0))
+       if (fsck_walk(obj, mark_used, NULL))
                objerror(obj, "broken links");
        if (fsck_object(obj, check_strict, fsck_error_func))
                return -1;
@@ -589,8 +589,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        struct alternate_object_database *alt;
 
        errors_found = 0;
+       read_replace_refs = 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..1df25b07b573301bba7fdcb3b13d1af9050f513e 100644 (file)
 #include "tag.h"
 #include "tree-walk.h"
 #include "builtin.h"
+#include "parse-options.h"
+#include "userdiff.h"
 #include "grep.h"
+#include "quote.h"
 
 #ifndef NO_EXTERNAL_GREP
 #ifdef __unix__
 #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)
 {
        struct grep_opt *opt = cb;
 
+       switch (userdiff_config(var, value)) {
+       case 0: break;
+       case -1: return -1;
+       default: return 0;
+       }
+
        if (!strcmp(var, "color.grep")) {
                opt->color = git_config_colorbool(var, value, -1);
                return 0;
@@ -41,26 +53,58 @@ static int grep_config(const char *var, const char *value, void *cb)
        return git_color_default_config(var, value, cb);
 }
 
+/*
+ * Return non-zero if max_depth is negative or path has no more then max_depth
+ * slashes.
+ */
+static int accept_subdir(const char *path, int max_depth)
+{
+       if (max_depth < 0)
+               return 1;
+
+       while ((path = strchr(path, '/')) != NULL) {
+               max_depth--;
+               if (max_depth < 0)
+                       return 0;
+               path++;
+       }
+       return 1;
+}
+
+/*
+ * Return non-zero if name is a subdirectory of match and is not too deep.
+ */
+static int is_subdir(const char *name, int namelen,
+               const char *match, int matchlen, int max_depth)
+{
+       if (matchlen > namelen || strncmp(name, match, matchlen))
+               return 0;
+
+       if (name[matchlen] == '\0') /* exact match */
+               return 1;
+
+       if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')
+               return accept_subdir(name + matchlen + 1, max_depth);
+
+       return 0;
+}
+
 /*
  * git grep pathspecs are somewhat different from diff-tree pathspecs;
  * pathname wildcards are allowed.
  */
-static int pathspec_matches(const char **paths, const char *name)
+static int pathspec_matches(const char **paths, const char *name, int max_depth)
 {
        int namelen, i;
        if (!paths || !*paths)
-               return 1;
+               return accept_subdir(name, max_depth);
        namelen = strlen(name);
        for (i = 0; paths[i]; i++) {
                const char *match = paths[i];
                int matchlen = strlen(match);
                const char *cp, *meta;
 
-               if (!matchlen ||
-                   ((matchlen <= namelen) &&
-                    !strncmp(name, match, matchlen) &&
-                    (match[matchlen-1] == '/' ||
-                     name[matchlen] == '\0' || name[matchlen] == '/')))
+               if (is_subdir(name, namelen, match, matchlen, max_depth))
                        return 1;
                if (!fnmatch(match, name, 0))
                        return 1;
@@ -114,8 +158,8 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char
        unsigned long size;
        char *data;
        enum object_type type;
-       char *to_free = NULL;
        int hit;
+       struct strbuf pathbuf = STRBUF_INIT;
 
        data = read_sha1_file(sha1, &type, &size);
        if (!data) {
@@ -123,26 +167,13 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char
                return 0;
        }
        if (opt->relative && opt->prefix_length) {
-               static char name_buf[PATH_MAX];
-               char *cp;
-               int name_len = strlen(name) - opt->prefix_length + 1;
-
-               if (!tree_name_len)
-                       name += opt->prefix_length;
-               else {
-                       if (ARRAY_SIZE(name_buf) <= name_len)
-                               cp = to_free = xmalloc(name_len);
-                       else
-                               cp = name_buf;
-                       memcpy(cp, name, tree_name_len);
-                       strcpy(cp + tree_name_len,
-                              name + tree_name_len + opt->prefix_length);
-                       name = cp;
-               }
+               quote_path_relative(name + tree_name_len, -1, &pathbuf, opt->prefix);
+               strbuf_insert(&pathbuf, 0, name, tree_name_len);
+               name = pathbuf.buf;
        }
        hit = grep_buffer(opt, name, data, size);
+       strbuf_release(&pathbuf);
        free(data);
-       free(to_free);
        return hit;
 }
 
@@ -152,6 +183,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
        int i;
        char *data;
        size_t sz;
+       struct strbuf buf = STRBUF_INIT;
 
        if (lstat(filename, &st) < 0) {
        err_ret:
@@ -176,8 +208,9 @@ static int grep_file(struct grep_opt *opt, const char *filename)
        }
        close(i);
        if (opt->relative && opt->prefix_length)
-               filename += opt->prefix_length;
+               filename = quote_path_relative(filename, -1, &buf, opt->prefix);
        i = grep_buffer(opt, filename, data, sz);
+       strbuf_release(&buf);
        free(data);
        return i;
 }
@@ -274,6 +307,17 @@ static int flush_grep(struct grep_opt *opt,
                argc -= 2;
        }
 
+       if (opt->pre_context || opt->post_context) {
+               /*
+                * grep handles hunk marks between files, but we need to
+                * do that ourselves between multiple calls.
+                */
+               if (opt->show_hunk_mark)
+                       write_or_die(1, "--\n", 3);
+               else
+                       opt->show_hunk_mark = 1;
+       }
+
        status = exec_grep(argc, argv);
 
        if (kept_0) {
@@ -399,7 +443,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                int kept;
                if (!S_ISREG(ce->ce_mode))
                        continue;
-               if (!pathspec_matches(paths, ce->name))
+               if (!pathspec_matches(paths, ce->name, opt->max_depth))
                        continue;
                name = ce->name;
                if (name[0] == '-') {
@@ -432,7 +476,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,10 +489,11 @@ 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;
+               hit = 0;
        }
 #endif
 
@@ -455,7 +501,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
                struct cache_entry *ce = active_cache[nr];
                if (!S_ISREG(ce->ce_mode))
                        continue;
-               if (!pathspec_matches(paths, ce->name))
+               if (!pathspec_matches(paths, ce->name, opt->max_depth))
                        continue;
                /*
                 * If CE_VALID is on, we assume worktree file and its cache entry
@@ -515,7 +561,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
                        strbuf_addch(&pathbuf, '/');
 
                down = pathbuf.buf + tn_len;
-               if (!pathspec_matches(paths, down))
+               if (!pathspec_matches(paths, down, opt->max_depth))
                        ;
                else if (S_ISREG(entry.mode))
                        hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
@@ -560,32 +606,196 @@ 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 = STRBUF_INIT;
+
+       patterns = fopen(arg, "r");
+       if (!patterns)
+               die_errno("cannot open '%s'", arg);
+       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 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 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 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),
+               { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth",
+                       "descend at most <depth> levels", PARSE_OPT_NONEG,
+                       NULL, 1 },
+               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_BOOLEAN('p', "show-function", &opt.funcname,
+                       "show a line with the function name before matches"),
+               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 = prefix;
        opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
        opt.relative = 1;
        opt.pathname = 1;
        opt.pattern_tail = &opt.pattern_list;
        opt.regflags = REG_NEWLINE;
+       opt.max_depth = -1;
 
        strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
        opt.color = -1;
@@ -603,227 +813,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;
+       if ((opt.color && !opt.color_external) || opt.funcname)
+               external_grep_allowed = 0;
        if (!opt.pattern_list)
                die("no pattern given.");
        if ((opt.regflags != REG_NEWLINE) && opt.fixed)
@@ -831,7 +835,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? */
@@ -856,15 +860,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        verify_filename(prefix, argv[j]);
        }
 
-       if (i < argc) {
+       if (i < argc)
                paths = get_pathspec(prefix, argv + i);
-               if (opt.prefix_length && opt.relative) {
-                       /* Make sure we do not get outside of paths */
-                       for (i = 0; paths[i]; i++)
-                               if (strncmp(prefix, paths[i], opt.prefix_length))
-                                       die("git grep: cannot generate relative filenames containing '..'");
-               }
-       }
        else if (prefix) {
                paths = xcalloc(2, sizeof(const char *));
                paths[0] = prefix;
@@ -874,7 +871,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..e1eba778a526f500b0a65a6b112e468f04a3f869 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);
@@ -398,7 +394,7 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
  * HTML.
  */
 #ifndef open_html
-void open_html(const char *path)
+static void open_html(const char *path)
 {
        execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
 }
@@ -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 d1fa12a59efb34256b2cc80b03c637cc844d84ff..dd84caecbc2a07bca90c8524157d50a8fd5ae316 100644 (file)
@@ -6,6 +6,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "exec_cmd.h"
+#include "parse-options.h"
 
 #ifndef DEFAULT_GIT_TEMPLATE_DIR
 #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
@@ -61,20 +62,20 @@ static void copy_templates_1(char *path, int baselen,
                memcpy(template + template_baselen, de->d_name, namelen+1);
                if (lstat(path, &st_git)) {
                        if (errno != ENOENT)
-                               die("cannot stat %s", path);
+                               die_errno("cannot stat '%s'", path);
                }
                else
                        exists = 1;
 
                if (lstat(template, &st_template))
-                       die("cannot stat template %s", template);
+                       die_errno("cannot stat template '%s'", template);
 
                if (S_ISDIR(st_template.st_mode)) {
                        DIR *subdir = opendir(template);
                        int baselen_sub = baselen + namelen;
                        int template_baselen_sub = template_baselen + namelen;
                        if (!subdir)
-                               die("cannot opendir %s", template);
+                               die_errno("cannot opendir '%s'", template);
                        path[baselen_sub++] =
                                template[template_baselen_sub++] = '/';
                        path[baselen_sub] =
@@ -91,16 +92,17 @@ static void copy_templates_1(char *path, int baselen,
                        int len;
                        len = readlink(template, lnk, sizeof(lnk));
                        if (len < 0)
-                               die("cannot readlink %s", template);
+                               die_errno("cannot readlink '%s'", template);
                        if (sizeof(lnk) <= len)
                                die("insanely long symlink %s", template);
                        lnk[len] = 0;
                        if (symlink(lnk, path))
-                               die("cannot symlink %s %s", lnk, path);
+                               die_errno("cannot symlink '%s' '%s'", lnk, path);
                }
                else if (S_ISREG(st_template.st_mode)) {
                        if (copy_file(path, template, st_template.st_mode))
-                               die("cannot copy %s to %s", template, path);
+                               die_errno("cannot copy '%s' to '%s'", template,
+                                         path);
                }
                else
                        error("ignoring template %s", template);
@@ -350,7 +352,7 @@ static int guess_repository_type(const char *git_dir)
        if (!strcmp(".", git_dir))
                return 1;
        if (!getcwd(cwd, sizeof(cwd)))
-               die("cannot tell cwd");
+               die_errno("cannot tell cwd");
        if (!strcmp(git_dir, cwd))
                return 1;
        /*
@@ -369,8 +371,16 @@ static int guess_repository_type(const char *git_dir)
        return 1;
 }
 
-static const char init_db_usage[] =
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]]";
+static int shared_callback(const struct option *opt, const char *arg, int unset)
+{
+       *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
+       return 0;
+}
+
+static const char *const init_db_usage[] = {
+       "git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]",
+       NULL
+};
 
 /*
  * If you want to, you can share the DB area with any number of branches.
@@ -383,25 +393,60 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        const char *git_dir;
        const char *template_dir = NULL;
        unsigned int flags = 0;
-       int i;
-
-       for (i = 1; i < argc; i++, argv++) {
-               const char *arg = argv[1];
-               if (!prefixcmp(arg, "--template="))
-                       template_dir = arg+11;
-               else if (!strcmp(arg, "--bare")) {
-                       static char git_dir[PATH_MAX+1];
-                       is_bare_repository_cfg = 1;
-                       setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir,
-                                               sizeof(git_dir)), 0);
-               } else if (!strcmp(arg, "--shared"))
-                       init_shared_repository = PERM_GROUP;
-               else if (!prefixcmp(arg, "--shared="))
-                       init_shared_repository = git_config_perm("arg", arg+9);
-               else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
-                       flags |= INIT_DB_QUIET;
-               else
-                       usage(init_db_usage);
+       const struct option init_db_options[] = {
+               OPT_STRING(0, "template", &template_dir, "template-directory",
+                               "provide the directory from which templates will be used"),
+               OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
+                               "create a bare repository", 1),
+               { OPTION_CALLBACK, 0, "shared", &init_shared_repository,
+                       "permissions",
+                       "specify that the git repository is to be shared amongst several users",
+                       PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
+               OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
+
+       if (argc == 1) {
+               int mkdir_tried = 0;
+       retry:
+               if (chdir(argv[0]) < 0) {
+                       if (!mkdir_tried) {
+                               int saved;
+                               /*
+                                * At this point we haven't read any configuration,
+                                * and we know shared_repository should always be 0;
+                                * but just in case we play safe.
+                                */
+                               saved = shared_repository;
+                               shared_repository = 0;
+                               switch (safe_create_leading_directories_const(argv[0])) {
+                               case -3:
+                                       errno = EEXIST;
+                                       /* fallthru */
+                               case -1:
+                                       die_errno("cannot mkdir %s", argv[0]);
+                                       break;
+                               default:
+                                       break;
+                               }
+                               shared_repository = saved;
+                               if (mkdir(argv[0], 0777) < 0)
+                                       die_errno("cannot mkdir %s", argv[0]);
+                               mkdir_tried = 1;
+                               goto retry;
+                       }
+                       die_errno("cannot chdir to %s", argv[0]);
+               }
+       } else if (0 < argc) {
+               usage(init_db_usage[0]);
+       }
+       if (is_bare_repository_cfg == 1) {
+               static char git_dir[PATH_MAX+1];
+
+               setenv(GIT_DIR_ENVIRONMENT,
+                       getcwd(git_dir, sizeof(git_dir)), 0);
        }
 
        if (init_shared_repository != -1)
@@ -440,11 +485,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                if (!git_work_tree_cfg) {
                        git_work_tree_cfg = xcalloc(PATH_MAX, 1);
                        if (!getcwd(git_work_tree_cfg, PATH_MAX))
-                               die ("Cannot access current working directory.");
+                               die_errno ("Cannot access current working directory");
                }
                if (access(get_git_work_tree(), X_OK))
-                       die ("Cannot access work tree '%s'",
-                            get_git_work_tree());
+                       die_errno ("Cannot access work tree '%s'",
+                                  get_git_work_tree());
        }
 
        set_git_dir(make_absolute_path(git_dir));
index 5eaec5d24e6ca70af2879ce9439eb2ba831211b6..25e21ed41534c8c3067c2e0d5afe4284faa2881a 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;
@@ -26,10 +27,15 @@ static int default_show_root = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
+static const char * const builtin_log_usage =
+       "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
+       "   or: git show [options] <object>...";
+
 static void cmd_log_init(int argc, const char **argv, const char *prefix,
                      struct rev_info *rev)
 {
        int i;
+       int decoration_style = 0;
 
        rev->abbrev = DEFAULT_ABBREV;
        rev->commit_format = CMIT_FMT_DEFAULT;
@@ -56,13 +62,26 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (!strcmp(arg, "--decorate")) {
-                       load_ref_decorations();
-                       rev->show_decorations = 1;
+                       decoration_style = DECORATE_SHORT_REFS;
+               } else if (!prefixcmp(arg, "--decorate=")) {
+                       const char *v = skip_prefix(arg, "--decorate=");
+                       if (!strcmp(v, "full"))
+                               decoration_style = DECORATE_FULL_REFS;
+                       else if (!strcmp(v, "short"))
+                               decoration_style = DECORATE_SHORT_REFS;
+                       else
+                               die("invalid --decorate option: %s", arg);
                } else if (!strcmp(arg, "--source")) {
                        rev->show_source = 1;
+               } else if (!strcmp(arg, "-h")) {
+                       usage(builtin_log_usage);
                } else
                        die("unrecognized argument: %s", arg);
        }
+       if (decoration_style) {
+               rev->show_decorations = 1;
+               load_ref_decorations(decoration_style);
+       }
 }
 
 /*
@@ -93,7 +112,7 @@ static void show_early_header(struct rev_info *rev, const char *stage, int nr)
        printf("Final output: %d %s\n", nr, stage);
 }
 
-struct itimerval early_output_timer;
+static struct itimerval early_output_timer;
 
 static void log_show_early(struct rev_info *revs, struct commit_list *list)
 {
@@ -256,7 +275,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)
        pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
                git_log_output_encoding ?
                git_log_output_encoding: git_commit_encoding);
-       printf("%s\n", out.buf);
+       printf("%s", out.buf);
        strbuf_release(&out);
 }
 
@@ -328,11 +347,14 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                case OBJ_TAG: {
                        struct tag *t = (struct tag *)o;
 
+                       if (rev.shown_one)
+                               putchar('\n');
                        printf("%stag %s%s\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                        ret = show_object(o->sha1, 1, &rev);
+                       rev.shown_one = 1;
                        if (ret)
                                break;
                        o = parse_object(t->tagged->sha1);
@@ -344,12 +366,15 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                        break;
                }
                case OBJ_TREE:
+                       if (rev.shown_one)
+                               putchar('\n');
                        printf("%stree %s%s\n\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        name,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                        read_tree_recursive((struct tree *)o, "", 0, 0, NULL,
                                        show_tree_object, NULL);
+                       rev.shown_one = 1;
                        break;
                case OBJ_COMMIT:
                        rev.pending.nr = rev.pending.alloc = 0;
@@ -619,7 +644,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;
@@ -657,6 +682,10 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        log_write_email_headers(rev, head, &subject_start, &extra_headers,
                                &need_8bit_cte);
 
+       for (i = 0; !need_8bit_cte && i < nr; i++)
+               if (has_non_ascii(list[i]->buffer))
+                       need_8bit_cte = 1;
+
        msg = body;
        pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
                     encoding);
@@ -740,17 +769,119 @@ 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;
@@ -760,6 +891,57 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        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);
@@ -782,100 +964,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;
-               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;
@@ -914,10 +1005,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                strbuf_addch(&buf, '\n');
        }
 
-       rev.extra_headers = strbuf_detach(&buf, 0);
+       rev.extra_headers = strbuf_detach(&buf, NULL);
 
        if (start_number < 0)
                start_number = 1;
+
+       /*
+        * If numbered is set solely due to format.numbered in config,
+        * and it would conflict with --keep-subject (-k) from the
+        * command line, reset "numbered".
+        */
+       if (numbered && keep_subject && !numbered_cmdline_opt)
+               numbered = 0;
+
        if (numbered && keep_subject)
                die ("-n and -k are mutually exclusive.");
        if (keep_subject && subject_prefix)
@@ -941,8 +1041,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                if (use_stdout)
                        die("standard output, or directory, which one?");
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
-                       die("Could not create directory %s",
-                           output_directory);
+                       die_errno("Could not create directory '%s'",
+                                 output_directory);
        }
 
        if (rev.pending.nr == 1) {
index da2daf45acd2154b7f00da34d41a5ab8acaf0c38..c5c0407b0b824327b0bbc6ae50ae1a030761b2b9 100644 (file)
@@ -161,12 +161,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
 
        /* For cached/deleted files we don't need to even do the readdir */
        if (show_others || show_killed) {
-               const char *path = ".", *base = "";
-               int baselen = prefix_len;
-
-               if (baselen)
-                       path = base = prefix;
-               read_directory(dir, path, base, baselen, pathspec);
+               fill_directory(dir, pathspec);
                if (show_others)
                        show_other_files(dir);
                if (show_killed)
@@ -175,10 +170,6 @@ static void show_files(struct dir_struct *dir, const char *prefix)
        if (show_cached | show_stage) {
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
-                       int dtype = ce_to_dtype(ce);
-                       if (excluded(dir, ce->name, &dtype) !=
-                                       !!(dir->flags & DIR_SHOW_IGNORED))
-                               continue;
                        if (show_unmerged && !ce_stage(ce))
                                continue;
                        if (ce->ce_flags & CE_UPDATE)
@@ -191,10 +182,6 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                        struct cache_entry *ce = active_cache[i];
                        struct stat st;
                        int err;
-                       int dtype = ce_to_dtype(ce);
-                       if (excluded(dir, ce->name, &dtype) !=
-                                       !!(dir->flags & DIR_SHOW_IGNORED))
-                               continue;
                        if (ce->ce_flags & CE_UPDATE)
                                continue;
                        err = lstat(ce->name, &st);
@@ -454,7 +441,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 +473,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 ";
@@ -529,11 +516,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                ps_matched = xcalloc(1, num);
        }
 
-       if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) {
-               fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
-                       argv[0]);
-               exit(1);
-       }
+       if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given)
+               die("ls-files --ignored needs some exclude pattern");
 
        /* With no flags, we default to showing the cached files */
        if (!(show_stage | show_deleted | show_others | show_unmerged |
index 1eeeb4de6d0d54e3fd753b7f057351094e10a24e..c90cd312ac99fe9a2169664e86a089b5378cacbe 100644 (file)
@@ -25,6 +25,7 @@ static enum  {
 static struct strbuf charset = STRBUF_INIT;
 static int patch_lines;
 static struct strbuf **p_hdr_data, **s_hdr_data;
+static int use_scissors;
 
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
@@ -193,8 +194,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 +481,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 +494,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 +550,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] != '?')
@@ -714,6 +713,56 @@ static inline int patchbreak(const struct strbuf *line)
        return 0;
 }
 
+static int is_scissors_line(const struct strbuf *line)
+{
+       size_t i, len = line->len;
+       int scissors = 0, gap = 0;
+       int first_nonblank = -1;
+       int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
+       const char *buf = line->buf;
+
+       for (i = 0; i < len; i++) {
+               if (isspace(buf[i])) {
+                       if (in_perforation) {
+                               perforation++;
+                               gap++;
+                       }
+                       continue;
+               }
+               last_nonblank = i;
+               if (first_nonblank < 0)
+                       first_nonblank = i;
+               if (buf[i] == '-') {
+                       in_perforation = 1;
+                       perforation++;
+                       continue;
+               }
+               if (i + 1 < len &&
+                   (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2))) {
+                       in_perforation = 1;
+                       perforation += 2;
+                       scissors += 2;
+                       i++;
+                       continue;
+               }
+               in_perforation = 0;
+       }
+
+       /*
+        * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+        * Even though there can be arbitrary cruft on the same line
+        * (e.g. "cut here"), in order to avoid misidentification, the
+        * perforation must occupy more than a third of the visible
+        * width of the line, and dashes and scissors must occupy more
+        * than half of the perforation.
+        */
+
+       visible = last_nonblank - first_nonblank + 1;
+       return (scissors && 8 <= visible &&
+               visible < perforation * 3 &&
+               gap * 2 < perforation);
+}
+
 static int handle_commit_msg(struct strbuf *line)
 {
        static int still_looking = 1;
@@ -725,7 +774,8 @@ static int handle_commit_msg(struct strbuf *line)
                strbuf_ltrim(line);
                if (!line->len)
                        return 0;
-               if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
+               still_looking = check_header(line, s_hdr_data, 0);
+               if (still_looking)
                        return 0;
        }
 
@@ -733,6 +783,26 @@ static int handle_commit_msg(struct strbuf *line)
        if (metainfo_charset)
                convert_to_utf8(line, charset.buf);
 
+       if (use_scissors && is_scissors_line(line)) {
+               int i;
+               if (fseek(cmitmsg, 0L, SEEK_SET))
+                       die_errno("Could not rewind output message file");
+               if (ftruncate(fileno(cmitmsg), 0))
+                       die_errno("Could not truncate output message file at scissors");
+               still_looking = 1;
+
+               /*
+                * We may have already read "secondary headers"; purge
+                * them to give ourselves a clean restart.
+                */
+               for (i = 0; header[i]; i++) {
+                       if (s_hdr_data[i])
+                               strbuf_release(s_hdr_data[i]);
+                       s_hdr_data[i] = NULL;
+               }
+               return 0;
+       }
+
        if (patchbreak(line)) {
                fclose(cmitmsg);
                cmitmsg = NULL;
@@ -767,7 +837,6 @@ static void handle_filter(struct strbuf *line)
 
 static void handle_body(void)
 {
-       int len = 0;
        struct strbuf prev = STRBUF_INIT;
 
        /* Skip up to the first boundary */
@@ -777,8 +846,6 @@ static void handle_body(void)
        }
 
        do {
-               strbuf_setlen(&line, line.len + len);
-
                /* process any boundary lines */
                if (*content_top && is_multipart_boundary(&line)) {
                        /* flush any leftover */
@@ -834,10 +901,7 @@ static void handle_body(void)
                        handle_filter(&line);
                }
 
-               strbuf_reset(&line);
-               if (strbuf_avail(&line) < 100)
-                       strbuf_grow(&line, 100);
-       } while ((len = read_line_with_nul(line.buf, strbuf_avail(&line), fin)));
+       } while (!strbuf_getwholeline(&line, fin, '\n'));
 
 handle_body_out:
        strbuf_release(&prev);
@@ -893,12 +957,9 @@ static void handle_info(void)
        fprintf(fout, "\n");
 }
 
-static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
-                   const char *msg, const char *patch)
+static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 {
        int peek;
-       keep_subject = ks;
-       metainfo_charset = encoding;
        fin = in;
        fout = out;
 
@@ -932,8 +993,20 @@ static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
        return 0;
 }
 
+static int git_mailinfo_config(const char *var, const char *value, void *unused)
+{
+       if (prefixcmp(var, "mailinfo."))
+               return git_default_config(var, value, unused);
+       if (!strcmp(var, "mailinfo.scissors")) {
+               use_scissors = git_config_bool(var, value);
+               return 0;
+       }
+       /* perhaps others here */
+       return 0;
+}
+
 static const char mailinfo_usage[] =
-       "git mailinfo [-k] [-u | --encoding=<encoding> | -n] msg patch <mail >info";
+       "git mailinfo [-k] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
 
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
@@ -942,9 +1015,9 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
        /* NEEDSWORK: might want to do the optional .git/ directory
         * discovery
         */
-       git_config(git_default_config, NULL);
+       git_config(git_mailinfo_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] == '-') {
@@ -956,6 +1029,10 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
                        metainfo_charset = NULL;
                else if (!prefixcmp(argv[1], "--encoding="))
                        metainfo_charset = argv[1] + 11;
+               else if (!strcmp(argv[1], "--scissors"))
+                       use_scissors = 1;
+               else if (!strcmp(argv[1], "--no-scissors"))
+                       use_scissors = 0;
                else
                        usage(mailinfo_usage);
                argc--; argv++;
@@ -964,5 +1041,5 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
        if (argc != 3)
                usage(mailinfo_usage);
 
-       return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]);
+       return !!mailinfo(stdin, stdout, argv[1], argv[2]);
 }
index 71f3b3b8741e505fc652e6c74c75972f19211f71..dfe5b151e6e0030abf6248808d414b2fecc5fdb7 100644 (file)
@@ -7,6 +7,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "string-list.h"
+#include "strbuf.h"
 
 static const char git_mailsplit_usage[] =
 "git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
@@ -42,26 +43,8 @@ static int is_from_line(const char *line, int len)
        return 1;
 }
 
-/* Could be as small as 64, enough to hold a Unix "From " line. */
-static char buf[4096];
-
-/* We cannot use fgets() because our lines can contain NULs */
-int read_line_with_nul(char *buf, int size, FILE *in)
-{
-       int len = 0, c;
-
-       for (;;) {
-               c = getc(in);
-               if (c == EOF)
-                       break;
-               buf[len++] = c;
-               if (c == '\n' || len + 1 >= size)
-                       break;
-       }
-       buf[len] = '\0';
-
-       return len;
-}
+static struct strbuf buf = STRBUF_INIT;
+static int keep_cr;
 
 /* Called with the first line (potentially partial)
  * already in buf[] -- normally that should begin with
@@ -71,37 +54,39 @@ int read_line_with_nul(char *buf, int size, FILE *in)
 static int split_one(FILE *mbox, const char *name, int allow_bare)
 {
        FILE *output = NULL;
-       int len = strlen(buf);
        int fd;
        int status = 0;
-       int is_bare = !is_from_line(buflen);
+       int is_bare = !is_from_line(buf.buf, buf.len);
 
        if (is_bare && !allow_bare)
                goto corrupt;
 
        fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
        if (fd < 0)
-               die("cannot open output file %s", name);
-       output = fdopen(fd, "w");
+               die_errno("cannot open output file '%s'", name);
+       output = xfdopen(fd, "w");
 
        /* Copy it out, while searching for a line that begins with
         * "From " and having something that looks like a date format.
         */
        for (;;) {
-               int is_partial = len && buf[len-1] != '\n';
+               if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' &&
+                       buf.buf[buf.len-2] == '\r') {
+                       strbuf_setlen(&buf, buf.len-2);
+                       strbuf_addch(&buf, '\n');
+               }
 
-               if (fwrite(buf, 1, len, output) != len)
-                       die("cannot write output");
+               if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
+                       die_errno("cannot write output");
 
-               len = read_line_with_nul(buf, sizeof(buf), mbox);
-               if (len == 0) {
+               if (strbuf_getwholeline(&buf, mbox, '\n')) {
                        if (feof(mbox)) {
                                status = 1;
                                break;
                        }
-                       die("cannot read mbox");
+                       die_errno("cannot read mbox");
                }
-               if (!is_partial && !is_bare && is_from_line(buf, len))
+               if (!is_bare && is_from_line(buf.buf, buf.len))
                        break; /* done with one message */
        }
        fclose(output);
@@ -166,7 +151,7 @@ static int split_maildir(const char *maildir, const char *dir,
                        goto out;
                }
 
-               if (fgets(buf, sizeof(buf), f) == NULL) {
+               if (strbuf_getwholeline(&buf, f, '\n')) {
                        error("cannot read mail %s (%s)", file, strerror(errno));
                        goto out;
                }
@@ -203,7 +188,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
        } while (isspace(peek));
        ungetc(peek, f);
 
-       if (fgets(buf, sizeof(buf), f) == NULL) {
+       if (strbuf_getwholeline(&buf, f, '\n')) {
                /* empty stdin is OK */
                if (f != stdin) {
                        error("cannot read mbox %s", file);
@@ -248,6 +233,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
                        nr = strtol(arg+2, NULL, 10);
                } else if ( arg[1] == 'b' && !arg[2] ) {
                        allow_bare = 1;
+               } else if (!strcmp(arg, "--keep-cr")) {
+                       keep_cr = 1;
                } else if ( arg[1] == 'o' && arg[2] ) {
                        dir = arg+2;
                } else if ( arg[1] == '-' && !arg[2] ) {
index 03fc1c211453f1ed09ee2c6b71d438b0bfbf474f..54e7ec22370ce63150ddc93ebe252bea09f5064a 100644 (file)
@@ -23,7 +23,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 }
 
 static const char * const merge_base_usage[] = {
-       "git merge-base [--all] <commit-id> <commit-id>...",
+       "git merge-base [-a|--all] <commit> <commit>...",
        NULL
 };
 
@@ -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..b6b84286b26a4317dfd5185ae83fd861c6f9fa7d 100644 (file)
@@ -268,7 +268,7 @@ static void squash_message(void)
        printf("Squash commit -- not updating HEAD\n");
        fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
        if (fd < 0)
-               die("Could not write to %s", git_path("SQUASH_MSG"));
+               die_errno("Could not write to '%s'", git_path("SQUASH_MSG"));
 
        init_revisions(&rev, NULL);
        rev.ignore_merges = 1;
@@ -294,9 +294,9 @@ static void squash_message(void)
                        NULL, NULL, rev.date_mode, 0);
        }
        if (write(fd, out.buf, out.len) < 0)
-               die("Writing SQUASH_MSG: %s", strerror(errno));
+               die_errno("Writing SQUASH_MSG");
        if (close(fd))
-               die("Finishing SQUASH_MSG: %s", strerror(errno));
+               die_errno("Finishing SQUASH_MSG");
        strbuf_release(&out);
 }
 
@@ -358,6 +358,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
        struct strbuf buf = STRBUF_INIT;
        struct strbuf bname = STRBUF_INIT;
        const char *ptr;
+       char *found_ref;
        int len, early;
 
        strbuf_branchname(&bname, remote);
@@ -368,14 +369,17 @@ static void merge_name(const char *remote, struct strbuf *msg)
        if (!remote_head)
                die("'%s' does not point to a commit", remote);
 
-       strbuf_addstr(&buf, "refs/heads/");
-       strbuf_addstr(&buf, remote);
-       resolve_ref(buf.buf, branch_head, 0, 0);
-
-       if (!hashcmp(remote_head->sha1, branch_head)) {
-               strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
-                       sha1_to_hex(branch_head), remote);
-               goto cleanup;
+       if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
+               if (!prefixcmp(found_ref, "refs/heads/")) {
+                       strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+                                   sha1_to_hex(branch_head), remote);
+                       goto cleanup;
+               }
+               if (!prefixcmp(found_ref, "refs/remotes/")) {
+                       strbuf_addf(msg, "%s\t\tremote branch '%s' of .\n",
+                                   sha1_to_hex(branch_head), remote);
+                       goto cleanup;
+               }
        }
 
        /* See if remote matches <name>^^^.. or <name>~<number> */
@@ -409,7 +413,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
                strbuf_addstr(&truname, "refs/heads/");
                strbuf_addstr(&truname, remote);
                strbuf_setlen(&truname, truname.len - len);
-               if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
+               if (resolve_ref(truname.buf, buf_sha, 0, NULL)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
                                    sha1_to_hex(remote_head->sha1),
@@ -428,8 +432,8 @@ static void merge_name(const char *remote, struct strbuf *msg)
 
                fp = fopen(git_path("FETCH_HEAD"), "r");
                if (!fp)
-                       die("could not open %s for reading: %s",
-                               git_path("FETCH_HEAD"), strerror(errno));
+                       die_errno("could not open '%s' for reading",
+                                 git_path("FETCH_HEAD"));
                strbuf_getline(&line, fp, '\n');
                fclose(fp);
                ptr = strstr(line.buf, "\tnot-for-merge\t");
@@ -462,7 +466,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);
        }
@@ -594,7 +598,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                discard_cache();
                if (read_cache() < 0)
                        die("failed to read the cache");
-               return -ret;
+               return ret;
        }
 }
 
@@ -764,7 +768,8 @@ static int suggest_conflicts(void)
 
        fp = fopen(git_path("MERGE_MSG"), "a");
        if (!fp)
-               die("Could not open %s for writing", git_path("MERGE_MSG"));
+               die_errno("Could not open '%s' for writing",
+                         git_path("MERGE_MSG"));
        fprintf(fp, "\nConflicts:\n");
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
@@ -836,8 +841,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 +863,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;
@@ -1186,27 +1194,29 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                sha1_to_hex(j->item->object.sha1));
                fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
                if (fd < 0)
-                       die("Could open %s for writing",
-                               git_path("MERGE_HEAD"));
+                       die_errno("Could not open '%s' for writing",
+                                 git_path("MERGE_HEAD"));
                if (write_in_full(fd, buf.buf, buf.len) != buf.len)
-                       die("Could not write to %s", git_path("MERGE_HEAD"));
+                       die_errno("Could not write to '%s'", git_path("MERGE_HEAD"));
                close(fd);
                strbuf_addch(&merge_msg, '\n');
                fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
                if (fd < 0)
-                       die("Could open %s for writing", git_path("MERGE_MSG"));
+                       die_errno("Could not open '%s' for writing",
+                                 git_path("MERGE_MSG"));
                if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
                        merge_msg.len)
-                       die("Could not write to %s", git_path("MERGE_MSG"));
+                       die_errno("Could not write to '%s'", git_path("MERGE_MSG"));
                close(fd);
                fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
                if (fd < 0)
-                       die("Could open %s for writing", git_path("MERGE_MODE"));
+                       die_errno("Could not open '%s' for writing",
+                                 git_path("MERGE_MODE"));
                strbuf_reset(&buf);
                if (!allow_fast_forward)
                        strbuf_addf(&buf, "no-ff");
                if (write_in_full(fd, buf.buf, buf.len) != buf.len)
-                       die("Could not write to %s", git_path("MERGE_MODE"));
+                       die_errno("Could not write to '%s'", git_path("MERGE_MODE"));
                close(fd);
        }
 
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..1b20028c67dec2f9954cb107620bd863b4b3a264 100644 (file)
@@ -24,14 +24,10 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
        result[count] = NULL;
        for (i = 0; i < count; i++) {
                int length = strlen(result[i]);
-               if (length > 0 && result[i][length - 1] == '/') {
+               if (length > 0 && is_dir_sep(result[i][length - 1]))
                        result[i] = xmemdupz(result[i], length - 1);
-               }
-               if (base_name) {
-                       const char *last_slash = strrchr(result[i], '/');
-                       if (last_slash)
-                               result[i] = last_slash + 1;
-               }
+               if (base_name)
+                       result[i] = basename((char *)result[i]);
        }
        return get_pathspec(prefix, result);
 }
@@ -57,7 +53,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
        struct option builtin_mv_options[] = {
                OPT__DRY_RUN(&show_only),
-               OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"),
+               OPT_BOOLEAN('f', "force", &force, "force move/rename even if target exists"),
                OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
                OPT_END(),
        };
@@ -72,7 +68,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);
 
@@ -208,7 +205,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        printf("Renaming %s to %s\n", src, dst);
                if (!show_only && mode != INDEX &&
                                rename(src, dst) < 0 && !ignore_errors)
-                       die ("renaming %s failed: %s", src, strerror(errno));
+                       die_errno ("renaming '%s' failed", src);
 
                if (mode == WORKING_DIRECTORY)
                        continue;
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..02f9246cdb29903e9d7aafe47604f6882a3e7f86 100644 (file)
 #include <pthread.h>
 #endif
 
-static const char pack_usage[] = "\
-git pack-objects [{ -q | --progress | --all-progress }] \n\
-       [--max-pack-size=N] [--local] [--incremental] \n\
-       [--window=N] [--window-memory=N] [--depth=N] \n\
-       [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
-       [--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
-       [--stdout | base-name] [--include-tag] \n\
-       [--keep-unreachable | --unpack-unreachable] \n\
-       [<ref-list | <object-list]";
+static const char pack_usage[] =
+  "git pack-objects [{ -q | --progress | --all-progress }]\n"
+  "        [--max-pack-size=N] [--local] [--incremental]\n"
+  "        [--window=N] [--window-memory=N] [--depth=N]\n"
+  "        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset]\n"
+  "        [--threads=N] [--non-empty] [--revs [--unpacked | --all]*]\n"
+  "        [--reflog] [--stdout | base-name] [--include-tag]\n"
+  "        [--keep-unreachable | --unpack-unreachable \n"
+  "        [<ref-list | <object-list]";
 
 struct object_entry {
        struct pack_idx_entry idx;
@@ -86,7 +86,7 @@ static int pack_compression_level = Z_DEFAULT_COMPRESSION;
 static int pack_compression_seen;
 
 static unsigned long delta_cache_size = 0;
-static unsigned long max_delta_cache_size = 0;
+static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
 static unsigned long cache_max_small_delta_size = 1000;
 
 static unsigned long window_memory_limit = 0;
@@ -536,11 +536,9 @@ static void write_pack_file(void)
                                 base_name, sha1_to_hex(sha1));
                        free_pack_by_name(tmpname);
                        if (adjust_perm(pack_tmp_name, mode))
-                               die("unable to make temporary pack file readable: %s",
-                                   strerror(errno));
+                               die_errno("unable to make temporary pack file readable");
                        if (rename(pack_tmp_name, tmpname))
-                               die("unable to rename temporary pack file: %s",
-                                   strerror(errno));
+                               die_errno("unable to rename temporary pack file");
 
                        /*
                         * Packs are runtime accessed in their mtime
@@ -566,11 +564,9 @@ static void write_pack_file(void)
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
                                 base_name, sha1_to_hex(sha1));
                        if (adjust_perm(idx_tmp_name, mode))
-                               die("unable to make temporary index file readable: %s",
-                                   strerror(errno));
+                               die_errno("unable to make temporary index file readable");
                        if (rename(idx_tmp_name, tmpname))
-                               die("unable to rename temporary index file: %s",
-                                   strerror(errno));
+                               die_errno("unable to rename temporary index file");
 
                        free(idx_tmp_name);
                        free(pack_tmp_name);
@@ -653,8 +649,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;
@@ -1013,6 +1008,33 @@ static void add_preferred_base(unsigned char *sha1)
        it->pcache.tree_size = size;
 }
 
+static void cleanup_preferred_base(void)
+{
+       struct pbase_tree *it;
+       unsigned i;
+
+       it = pbase_tree;
+       pbase_tree = NULL;
+       while (it) {
+               struct pbase_tree *this = it;
+               it = this->next;
+               free(this->pcache.tree_data);
+               free(this);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) {
+               if (!pbase_tree_cache[i])
+                       continue;
+               free(pbase_tree_cache[i]->tree_data);
+               free(pbase_tree_cache[i]);
+               pbase_tree_cache[i] = NULL;
+       }
+
+       free(done_pbase_paths);
+       done_pbase_paths = NULL;
+       done_pbase_paths_num = done_pbase_paths_alloc = 0;
+}
+
 static void check_object(struct object_entry *entry)
 {
        if (entry->in_pack) {
@@ -1604,7 +1626,7 @@ static void *threaded_find_deltas(void *arg)
 static void ll_find_deltas(struct object_entry **list, unsigned list_size,
                           int window, int depth, unsigned *processed)
 {
-       struct thread_params p[delta_search_threads];
+       struct thread_params *p;
        int i, ret, active_threads = 0;
 
        if (delta_search_threads <= 1) {
@@ -1614,6 +1636,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
        if (progress > pack_to_stdout)
                fprintf(stderr, "Delta compression using up to %d threads.\n",
                                delta_search_threads);
+       p = xcalloc(delta_search_threads, sizeof(*p));
 
        /* Partition the work amongst work threads. */
        for (i = 0; i < delta_search_threads; i++) {
@@ -1722,6 +1745,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
                        active_threads--;
                }
        }
+       free(p);
 }
 
 #else
@@ -1813,7 +1837,7 @@ static void prepare_pack(int window, int depth)
 
 static int git_pack_config(const char *k, const char *v, void *cb)
 {
-       if(!strcmp(k, "pack.window")) {
+       if (!strcmp(k, "pack.window")) {
                window = git_config_int(k, v);
                return 0;
        }
@@ -1880,7 +1904,7 @@ static void read_object_list_from_stdin(void)
                        if (!ferror(stdin))
                                die("fgets returned NULL, not EOF, not error!");
                        if (errno != EINTR)
-                               die("fgets: %s", strerror(errno));
+                               die_errno("fgets");
                        clearerr(stdin);
                        continue;
                }
@@ -2103,6 +2127,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        int rp_ac_alloc = 64;
        int rp_ac;
 
+       read_replace_refs = 0;
+
        rp_av = xcalloc(rp_ac_alloc, sizeof(*rp_av));
 
        rp_av[0] = "pack-objects";
@@ -2260,6 +2286,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                                die("bad %s", arg);
                        continue;
                }
+               if (!strcmp(arg, "--keep-true-parents")) {
+                       grafts_replace_parents = 0;
+                       continue;
+               }
                usage(pack_usage);
        }
 
@@ -2309,6 +2339,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                rp_av[rp_ac] = NULL;
                get_object_list(rp_ac, rp_av);
        }
+       cleanup_preferred_base();
        if (include_tag && nr_result)
                for_each_ref(add_ref_tag, NULL);
        stop_progress(&progress_state);
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 4942892e9f83abc7beed6570d508cfe76da126a0..be99eb0ac4162411d63d10003fa80e12206b8cc6 100644 (file)
@@ -1,9 +1,12 @@
 #include "builtin.h"
 #include "cache.h"
 #include "progress.h"
+#include "parse-options.h"
 
-static const char prune_packed_usage[] =
-"git prune-packed [-n] [-q]";
+static const char * const prune_packed_usage[] = {
+       "git prune-packed [-n|--dry-run] [-q|--quiet]",
+       NULL
+};
 
 #define DRY_RUN 01
 #define VERBOSE 02
@@ -28,8 +31,8 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
                memcpy(pathname + len, de->d_name, 38);
                if (opts & DRY_RUN)
                        printf("rm -f %s\n", pathname);
-               else if (unlink(pathname) < 0)
-                       error("unable to unlink %s", pathname);
+               else
+                       unlink_or_warn(pathname);
                display_progress(progress, i + 1);
        }
        pathname[len] = 0;
@@ -68,24 +71,16 @@ void prune_packed_objects(int opts)
 
 int cmd_prune_packed(int argc, const char **argv, const char *prefix)
 {
-       int i;
        int opts = VERBOSE;
+       const struct option prune_packed_options[] = {
+               OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN),
+               OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE),
+               OPT_END()
+       };
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
+       argc = parse_options(argc, argv, prefix, prune_packed_options,
+                            prune_packed_usage, 0);
 
-               if (*arg == '-') {
-                       if (!strcmp(arg, "-n"))
-                               opts |= DRY_RUN;
-                       else if (!strcmp(arg, "-q"))
-                               opts &= ~VERBOSE;
-                       else
-                               usage(prune_packed_usage);
-                       continue;
-               }
-               /* Handle arguments here .. */
-               usage(prune_packed_usage);
-       }
        prune_packed_objects(opts);
        return 0;
 }
index 545e9c1f9458ed936e2a99a208f75b70f368294c..8459aec8e8ea9d24a13448cf950d2e160361fd9d 100644 (file)
@@ -27,7 +27,7 @@ static int prune_tmp_object(const char *path, const char *filename)
        }
        printf("Removing stale temporary file %s\n", fullpath);
        if (!show_only)
-               unlink(fullpath);
+               unlink_or_warn(fullpath);
        return 0;
 }
 
@@ -47,7 +47,7 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
                       (type > 0) ? typename(type) : "unknown");
        }
        if (!show_only)
-               unlink(fullpath);
+               unlink_or_warn(fullpath);
        return 0;
 }
 
@@ -140,9 +140,10 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        char *s;
 
        save_commit_buffer = 0;
+       read_replace_refs = 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..b5cd2cdad04548f053d0a9ff24e661d4c3ee1d39 100644 (file)
@@ -10,7 +10,7 @@
 #include "parse-options.h"
 
 static const char * const push_usage[] = {
-       "git push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
+       "git push [<options>] [<repository> <refspec>...]",
        NULL,
 };
 
@@ -64,36 +64,11 @@ static void setup_push_tracking(void)
        add_refspec(refspec.buf);
 }
 
-static const char *warn_unconfigured_push_msg[] = {
-       "You did not specify any refspecs to push, and the current remote",
-       "has not configured any push refspecs. The default action in this",
-       "case is to push all matching refspecs, that is, all branches",
-       "that exist both locally and remotely will be updated.  This may",
-       "not necessarily be what you want to happen.",
-       "",
-       "You can specify what action you want to take in this case, and",
-       "avoid seeing this message again, by configuring 'push.default' to:",
-       "  'nothing'  : Do not push anything",
-       "  'matching' : Push all matching branches (default)",
-       "  'tracking' : Push the current branch to whatever it is tracking",
-       "  'current'  : Push the current branch"
-};
-
-static void warn_unconfigured_push(void)
-{
-       int i;
-       for (i = 0; i < ARRAY_SIZE(warn_unconfigured_push_msg); i++)
-               warning("%s", warn_unconfigured_push_msg[i]);
-}
-
 static void setup_default_push_refspecs(void)
 {
        git_config(git_default_config, NULL);
        switch (push_default) {
-       case PUSH_DEFAULT_UNSPECIFIED:
-               warn_unconfigured_push();
-               /* fallthrough */
-
+       default:
        case PUSH_DEFAULT_MATCHING:
                add_refspec(":");
                break;
@@ -117,6 +92,8 @@ static int do_push(const char *repo, int flags)
 {
        int i, errs;
        struct remote *remote = remote_get(repo);
+       const char **url;
+       int url_nr;
 
        if (!remote) {
                if (repo)
@@ -152,10 +129,18 @@ static int do_push(const char *repo, int flags)
                        setup_default_push_refspecs();
        }
        errs = 0;
-       for (i = 0; i < remote->url_nr; i++) {
+       if (remote->pushurl_nr) {
+               url = remote->pushurl;
+               url_nr = remote->pushurl_nr;
+       } else {
+               url = remote->url;
+               url_nr = remote->url_nr;
+       }
+       for (i = 0; i < url_nr; i++) {
                struct transport *transport =
-                       transport_get(remote, remote->url[i]);
+                       transport_get(remote, url[i]);
                int err;
+               int nonfastforward;
                if (receivepack)
                        transport_set_option(transport,
                                             TRANS_OPT_RECEIVEPACK, receivepack);
@@ -163,14 +148,20 @@ static int do_push(const char *repo, int flags)
                        transport_set_option(transport, TRANS_OPT_THIN, "yes");
 
                if (flags & TRANSPORT_PUSH_VERBOSE)
-                       fprintf(stderr, "Pushing to %s\n", remote->url[i]);
-               err = transport_push(transport, refspec_nr, refspec, flags);
+                       fprintf(stderr, "Pushing to %s\n", url[i]);
+               err = transport_push(transport, refspec_nr, refspec, flags,
+                                    &nonfastforward);
                err |= transport_disconnect(transport);
 
                if (!err)
                        continue;
 
-               error("failed to push some refs to '%s'", remote->url[i]);
+               error("failed to push some refs to '%s'", url[i]);
+               if (nonfastforward && advice_push_nonfastforward) {
+                       printf("To prevent you from losing history, non-fast-forward updates were rejected\n"
+                              "Merge the remote changes before pushing again.  See the 'non-fast forward'\n"
+                              "section of 'git push --help' for details.\n");
+               }
                errs++;
        }
        return !!errs;
@@ -184,13 +175,15 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        const char *repo = NULL;        /* default repository */
 
        struct option options[] = {
+               OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET),
                OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE),
                OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
                OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
                OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
-               OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
-               OPT_BIT( 0 , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
+               OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror"),
+               OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
+               OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
                OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
                OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
                OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
@@ -198,7 +191,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 82e25eaa0758d8b9584592f4072f81eeccb0bf1d..14c836b1693317d5d834606e5613d7ceacb3189c 100644 (file)
@@ -12,6 +12,7 @@
 #include "unpack-trees.h"
 #include "dir.h"
 #include "builtin.h"
+#include "parse-options.h"
 
 static int nr_trees;
 static struct tree *trees[MAX_UNPACK_TREES];
@@ -29,7 +30,39 @@ static int list_tree(unsigned char *sha1)
        return 0;
 }
 
-static const char read_tree_usage[] = "git read-tree (<sha> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])";
+static const char * const read_tree_usage[] = {
+       "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]]  [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
+       NULL
+};
+
+static int index_output_cb(const struct option *opt, const char *arg,
+                                int unset)
+{
+       set_alternate_index_output(arg);
+       return 0;
+}
+
+static int exclude_per_directory_cb(const struct option *opt, const char *arg,
+                                   int unset)
+{
+       struct dir_struct *dir;
+       struct unpack_trees_options *opts;
+
+       opts = (struct unpack_trees_options *)opt->value;
+
+       if (opts->dir)
+               die("more than one --exclude-per-directory given.");
+
+       dir = xcalloc(1, sizeof(*opts->dir));
+       dir->flags |= DIR_SHOW_IGNORED;
+       dir->exclude_per_dir = arg;
+       opts->dir = dir;
+       /* We do not need to nor want to do read-directory
+        * here; we are merely interested in reusing the
+        * per directory ignore stack mechanism.
+        */
+       return 0;
+}
 
 static struct lock_file lock_file;
 
@@ -39,6 +72,34 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        unsigned char sha1[20];
        struct tree_desc t[MAX_UNPACK_TREES];
        struct unpack_trees_options opts;
+       int prefix_set = 0;
+       const struct option read_tree_options[] = {
+               { OPTION_CALLBACK, 0, "index-output", NULL, "FILE",
+                 "write resulting index to <FILE>",
+                 PARSE_OPT_NONEG, index_output_cb },
+               OPT__VERBOSE(&opts.verbose_update),
+               OPT_GROUP("Merging"),
+               OPT_SET_INT('m', NULL, &opts.merge,
+                           "perform a merge in addition to a read", 1),
+               OPT_SET_INT(0, "trivial", &opts.trivial_merges_only,
+                           "3-way merge if no file level merging required", 1),
+               OPT_SET_INT(0, "aggressive", &opts.aggressive,
+                           "3-way merge in presence of adds and removes", 1),
+               OPT_SET_INT(0, "reset", &opts.reset,
+                           "same as -m, but discard unmerged entries", 1),
+               { OPTION_STRING, 0, "prefix", &opts.prefix, "<subdirectory>/",
+                 "read the tree into the index under <subdirectory>/",
+                 PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP },
+               OPT_SET_INT('u', NULL, &opts.update,
+                           "update working tree with merge result", 1),
+               { OPTION_CALLBACK, 0, "exclude-per-directory", &opts,
+                 "gitignore",
+                 "allow explicitly ignored files to be overwritten",
+                 PARSE_OPT_NONEG, exclude_per_directory_cb },
+               OPT_SET_INT('i', NULL, &opts.index_only,
+                           "don't check the working tree after merging", 1),
+               OPT_END()
+       };
 
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = -1;
@@ -49,105 +110,21 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 
        newfd = hold_locked_index(&lock_file, 1);
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-
-               /* "-u" means "update", meaning that a merge will update
-                * the working tree.
-                */
-               if (!strcmp(arg, "-u")) {
-                       opts.update = 1;
-                       continue;
-               }
+       argc = parse_options(argc, argv, unused_prefix, read_tree_options,
+                            read_tree_usage, 0);
 
-               if (!strcmp(arg, "-v")) {
-                       opts.verbose_update = 1;
-                       continue;
-               }
+       prefix_set = opts.prefix ? 1 : 0;
+       if (1 < opts.merge + opts.reset + prefix_set)
+               die("Which one? -m, --reset, or --prefix?");
 
-               /* "-i" means "index only", meaning that a merge will
-                * not even look at the working tree.
-                */
-               if (!strcmp(arg, "-i")) {
-                       opts.index_only = 1;
-                       continue;
-               }
-
-               if (!prefixcmp(arg, "--index-output=")) {
-                       set_alternate_index_output(arg + 15);
-                       continue;
-               }
-
-               /* "--prefix=<subdirectory>/" means keep the current index
-                *  entries and put the entries from the tree under the
-                * given subdirectory.
-                */
-               if (!prefixcmp(arg, "--prefix=")) {
-                       if (stage || opts.merge || opts.prefix)
-                               usage(read_tree_usage);
-                       opts.prefix = arg + 9;
-                       opts.merge = 1;
-                       stage = 1;
-                       if (read_cache_unmerged())
-                               die("you need to resolve your current index first");
-                       continue;
-               }
-
-               /* This differs from "-m" in that we'll silently ignore
-                * unmerged entries and overwrite working tree files that
-                * correspond to them.
-                */
-               if (!strcmp(arg, "--reset")) {
-                       if (stage || opts.merge || opts.prefix)
-                               usage(read_tree_usage);
-                       opts.reset = 1;
-                       opts.merge = 1;
-                       stage = 1;
-                       read_cache_unmerged();
-                       continue;
-               }
-
-               if (!strcmp(arg, "--trivial")) {
-                       opts.trivial_merges_only = 1;
-                       continue;
-               }
-
-               if (!strcmp(arg, "--aggressive")) {
-                       opts.aggressive = 1;
-                       continue;
-               }
-
-               /* "-m" stands for "merge", meaning we start in stage 1 */
-               if (!strcmp(arg, "-m")) {
-                       if (stage || opts.merge || opts.prefix)
-                               usage(read_tree_usage);
-                       if (read_cache_unmerged())
-                               die("you need to resolve your current index first");
-                       stage = 1;
-                       opts.merge = 1;
-                       continue;
-               }
-
-               if (!prefixcmp(arg, "--exclude-per-directory=")) {
-                       struct dir_struct *dir;
-
-                       if (opts.dir)
-                               die("more than one --exclude-per-directory are given.");
-
-                       dir = xcalloc(1, sizeof(*opts.dir));
-                       dir->flags |= DIR_SHOW_IGNORED;
-                       dir->exclude_per_dir = arg + 24;
-                       opts.dir = dir;
-                       /* We do not need to nor want to do read-directory
-                        * here; we are merely interested in reusing the
-                        * per directory ignore stack mechanism.
-                        */
-                       continue;
-               }
+       if (opts.reset || opts.merge || opts.prefix) {
+               if (read_cache_unmerged() && (opts.prefix || opts.merge))
+                       die("You need to resolve your current index first");
+               stage = opts.merge = 1;
+       }
 
-               /* using -u and -i at the same time makes no sense */
-               if (1 < opts.index_only + opts.update)
-                       usage(read_tree_usage);
+       for (i = 0; i < argc; i++) {
+               const char *arg = argv[i];
 
                if (get_sha1(arg, sha1))
                        die("Not a valid object name %s", arg);
@@ -155,8 +132,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                        die("failed to unpack tree object %s", arg);
                stage++;
        }
+       if (1 < opts.index_only + opts.update)
+               die("-u and -i at the same time makes no sense");
        if ((opts.update||opts.index_only) && !opts.merge)
-               usage(read_tree_usage);
+               die("%s is meaningless without -m, --reset, or --prefix",
+                   opts.update ? "-u" : "-i");
        if ((opts.dir && !opts.update))
                die("--exclude-per-directory is meaningless unless -u");
        if (opts.merge && !opts.index_only)
index a970b39505b5dda0bfa32a1a9c14ad676d28ec92..b771fe9b20f4c4d6e19289f428442d489aba6896 100644 (file)
@@ -27,10 +27,9 @@ static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
 static int report_status;
+static int prefer_ofs_delta = 1;
 static const char *head_name;
-
-static char capabilities[] = " report-status delete-refs ";
-static int capabilities_sent;
+static char *capabilities_to_send;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
 {
@@ -84,24 +83,29 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
+               prefer_ofs_delta = git_config_bool(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
 static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       if (capabilities_sent)
+       if (!capabilities_to_send)
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
        else
                packet_write(1, "%s %s%c%s\n",
-                            sha1_to_hex(sha1), path, 0, capabilities);
-       capabilities_sent = 1;
+                            sha1_to_hex(sha1), path, 0, capabilities_to_send);
+       capabilities_to_send = NULL;
        return 0;
 }
 
 static void write_head_info(void)
 {
        for_each_ref(show_ref, NULL);
-       if (!capabilities_sent)
+       if (capabilities_to_send)
                show_ref("capabilities^{}", null_sha1, 0, NULL);
 
 }
@@ -119,31 +123,6 @@ static struct command *commands;
 static const char pre_receive_hook[] = "hooks/pre-receive";
 static const char post_receive_hook[] = "hooks/post-receive";
 
-static int hook_status(int code, const char *hook_name)
-{
-       switch (code) {
-       case 0:
-               return 0;
-       case -ERR_RUN_COMMAND_FORK:
-               return error("hook fork failed");
-       case -ERR_RUN_COMMAND_EXEC:
-               return error("hook execute failed");
-       case -ERR_RUN_COMMAND_PIPE:
-               return error("hook pipe failed");
-       case -ERR_RUN_COMMAND_WAITPID:
-               return error("waitpid failed");
-       case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-               return error("waitpid is confused");
-       case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-               return error("%s died of signal", hook_name);
-       case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-               return error("%s died strangely", hook_name);
-       default:
-               error("%s exited with error code %d", hook_name, -code);
-               return -code;
-       }
-}
-
 static int run_receive_hook(const char *hook_name)
 {
        static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
@@ -170,7 +149,7 @@ static int run_receive_hook(const char *hook_name)
 
        code = start_command(&proc);
        if (code)
-               return hook_status(code, hook_name);
+               return code;
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (!cmd->error_string) {
                        size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
@@ -182,13 +161,12 @@ static int run_receive_hook(const char *hook_name)
                }
        }
        close(proc.in);
-       return hook_status(finish_command(&proc), hook_name);
+       return finish_command(&proc);
 }
 
 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)
@@ -200,12 +178,8 @@ 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 run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
+                                       RUN_COMMAND_STDOUT_TO_STDERR);
 }
 
 static int is_ref_checked_out(const char *ref)
@@ -394,7 +368,7 @@ static char update_post_hook[] = "hooks/post-update";
 static void run_update_post_hook(struct command *cmd)
 {
        struct command *cmd_p;
-       int argc;
+       int argc, status;
        const char **argv;
 
        for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
@@ -417,8 +391,8 @@ static void run_update_post_hook(struct command *cmd)
                argc++;
        }
        argv[argc] = NULL;
-       run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
-               | RUN_COMMAND_STDOUT_TO_STDERR);
+       status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
+                       | RUN_COMMAND_STDOUT_TO_STDERR);
 }
 
 static void execute_commands(const char *unpacker_error)
@@ -534,24 +508,9 @@ static const char *unpack(void)
                unpacker[i++] = hdr_arg;
                unpacker[i++] = NULL;
                code = run_command_v_opt(unpacker, RUN_GIT_CMD);
-               switch (code) {
-               case 0:
+               if (!code)
                        return NULL;
-               case -ERR_RUN_COMMAND_FORK:
-                       return "unpack fork failed";
-               case -ERR_RUN_COMMAND_EXEC:
-                       return "unpack execute failed";
-               case -ERR_RUN_COMMAND_WAITPID:
-                       return "waitpid failed";
-               case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-                       return "waitpid is confused";
-               case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-                       return "unpacker died of signal";
-               case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-                       return "unpacker died strangely";
-               default:
-                       return "unpacker exited with error code";
-               }
+               return "unpack-objects abnormal exit";
        } else {
                const char *keeper[7];
                int s, status, i = 0;
@@ -574,8 +533,10 @@ static const char *unpack(void)
                ip.argv = keeper;
                ip.out = -1;
                ip.git_cmd = 1;
-               if (start_command(&ip))
+               status = start_command(&ip);
+               if (status) {
                        return "index-pack fork failed";
+               }
                pack_lockfile = index_pack_lockfile(ip.out);
                close(ip.out);
                status = finish_command(&ip);
@@ -687,6 +648,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        else if (0 <= receive_unpack_limit)
                unpack_limit = receive_unpack_limit;
 
+       capabilities_to_send = (prefer_ofs_delta) ?
+               " report-status delete-refs ofs-delta " :
+               " report-status delete-refs ";
+
        add_alternate_refs();
        write_head_info();
        clear_extra_refs();
@@ -702,7 +667,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        unpack_status = unpack();
                execute_commands(unpack_status);
                if (pack_lockfile)
-                       unlink(pack_lockfile);
+                       unlink_or_warn(pack_lockfile);
                if (report_status)
                        report(unpack_status);
                run_receive_hook(post_receive_hook);
index ddfdf5a3cbc79003dfbad14af8d1f047e3594aa6..e23b5ef979d98e5e44693345856a13485695246f 100644 (file)
@@ -362,7 +362,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
                } else if (cmd->updateref &&
                        (write_in_full(lock->lock_fd,
                                sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
-                        write_in_full(lock->lock_fd, "\n", 1) != 1 ||
+                        write_str_in_full(lock->lock_fd, "\n") != 1 ||
                         close_ref(lock) < 0)) {
                        status |= error("Couldn't write %s",
                                lock->lk->filename);
@@ -694,7 +694,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
  */
 
 static const char reflog_usage[] =
-"git reflog (expire | ...)";
+"git reflog [ show | expire | delete ]";
 
 int cmd_reflog(int argc, const char **argv, const char *prefix)
 {
index 2ed752cbf1803c243145ecc73d6abbf758a2203a..0777dd719b41ec4c545b336bc5144a73d34001d7 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();
-       ref = push_map = copy_ref_list(remote_refs);
-       while (ref->next)
-               ref = ref->next;
-       push_tail = &ref->next;
+       push_map = copy_ref_list(remote_refs);
 
-       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;
@@ -387,7 +385,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
        get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
        matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
                                    fetch_map, 1);
-       for(ref = matches; ref; ref = ref->next)
+       for (ref = matches; ref; ref = ref->next)
                string_list_append(abbrev_branch(ref->name), &states->heads);
 
        free_refs(fetch_map);
@@ -486,7 +484,7 @@ static int read_remote_branches(const char *refname,
        const char *symref;
 
        strbuf_addf(&buf, "refs/remotes/%s", rename->old);
-       if(!prefixcmp(refname, buf.buf)) {
+       if (!prefixcmp(refname, buf.buf)) {
                item = string_list_append(xstrdup(refname), rename->remote_branches);
                symref = resolve_ref(refname, orig_sha1, 1, &flag);
                if (flag & REF_ISSYMREF)
@@ -525,8 +523,8 @@ static int migrate_file(struct remote *remote)
                path = git_path("remotes/%s", remote->name);
        else if (remote->origin == REMOTE_BRANCHES)
                path = git_path("branches/%s", remote->name);
-       if (path && unlink(path))
-               warning("failed to remove '%s'", path);
+       if (path)
+               unlink_or_warn(path);
        return 0;
 }
 
@@ -742,7 +740,7 @@ static int rm(int argc, const char **argv)
        return result;
 }
 
-void clear_push_info(void *util, const char *string)
+static void clear_push_info(void *util, const char *string)
 {
        struct push_info *info = util;
        free(info->dest);
@@ -789,7 +787,7 @@ static int get_remote_ref_states(const char *name,
        read_branches();
 
        if (query) {
-               transport = transport_get(NULL, states->remote->url_nr > 0 ?
+               transport = transport_get(states->remote, states->remote->url_nr > 0 ?
                        states->remote->url[0] : NULL);
                remote_refs = transport_get_remote_refs(transport);
                transport_disconnect(transport);
@@ -817,7 +815,7 @@ struct show_info {
        int any_rebase;
 };
 
-int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
+static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
 {
        struct show_info *info = cb_data;
        int n = strlen(item->string);
@@ -827,7 +825,7 @@ int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
        return 0;
 }
 
-int show_remote_info_item(struct string_list_item *item, void *cb_data)
+static int show_remote_info_item(struct string_list_item *item, void *cb_data)
 {
        struct show_info *info = cb_data;
        struct ref_states *states = info->states;
@@ -854,7 +852,7 @@ int show_remote_info_item(struct string_list_item *item, void *cb_data)
        return 0;
 }
 
-int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
+static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
 {
        struct show_info *show_info = cb_data;
        struct ref_states *states = show_info->states;
@@ -876,7 +874,7 @@ int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
        return 0;
 }
 
-int show_local_info_item(struct string_list_item *item, void *cb_data)
+static int show_local_info_item(struct string_list_item *item, void *cb_data)
 {
        struct show_info *show_info = cb_data;
        struct branch_info *branch_info = item->util;
@@ -908,7 +906,7 @@ int show_local_info_item(struct string_list_item *item, void *cb_data)
        return 0;
 }
 
-int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
+static int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
 {
        struct show_info *show_info = cb_data;
        struct push_info *push_info = push_item->util;
@@ -937,7 +935,7 @@ static int cmp_string_with_push(const void *va, const void *vb)
        return cmp ? cmp : strcmp(a_push->dest, b_push->dest);
 }
 
-int show_push_info_item(struct string_list_item *item, void *cb_data)
+static int show_push_info_item(struct string_list_item *item, void *cb_data)
 {
        struct show_info *show_info = cb_data;
        struct push_info *push_info = item->util;
@@ -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();
@@ -1000,12 +999,25 @@ static int show(int argc, const char **argv)
        info.list = &info_list;
        for (; argc; argc--, argv++) {
                int i;
+               const char **url;
+               int url_nr;
 
                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);
+               printf("  Fetch URL: %s\n", states.remote->url_nr > 0 ?
+                       states.remote->url[0] : "(no URL)");
+               if (states.remote->pushurl_nr) {
+                       url = states.remote->pushurl;
+                       url_nr = states.remote->pushurl_nr;
+               } else {
+                       url = states.remote->url;
+                       url_nr = states.remote->url_nr;
+               }
+               for (i=0; i < url_nr; i++)
+                       printf("  Push  URL: %s\n", url[i]);
+               if (!i)
+                       printf("  Push  URL: %s\n", "(no URL)");
                if (no_query)
                        printf("  HEAD branch: (not queried)\n");
                else if (!states.heads.nr)
@@ -1076,7 +1088,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 +1143,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);
@@ -1183,7 +1197,7 @@ static int get_one_remote_for_update(struct remote *remote, void *priv)
        return 0;
 }
 
-struct remote_group {
+static struct remote_group {
        const char *name;
        struct string_list *list;
 } remote_group;
@@ -1220,7 +1234,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;
@@ -1262,14 +1276,29 @@ static int update(int argc, const char **argv)
 static int get_one_entry(struct remote *remote, void *priv)
 {
        struct string_list *list = priv;
+       struct strbuf url_buf = STRBUF_INIT;
+       const char **url;
+       int i, url_nr;
 
        if (remote->url_nr > 0) {
-               int i;
-
-               for (i = 0; i < remote->url_nr; i++)
-                       string_list_append(remote->name, list)->util = (void *)remote->url[i];
+               strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
+               string_list_append(remote->name, list)->util =
+                               strbuf_detach(&url_buf, NULL);
        } else
                string_list_append(remote->name, list)->util = NULL;
+       if (remote->pushurl_nr) {
+               url = remote->pushurl;
+               url_nr = remote->pushurl_nr;
+       } else {
+               url = remote->url;
+               url_nr = remote->url_nr;
+       }
+       for (i = 0; i < url_nr; i++)
+       {
+               strbuf_addf(&url_buf, "%s (push)", url[i]);
+               string_list_append(remote->name, list)->util =
+                               strbuf_detach(&url_buf, NULL);
+       }
 
        return 0;
 }
@@ -1277,7 +1306,10 @@ static int get_one_entry(struct remote *remote, void *priv)
 static int show_all(void)
 {
        struct string_list list = { NULL, 0, 0 };
-       int result = for_each_remote(get_one_entry, &list);
+       int result;
+
+       list.strdup_strings = 1;
+       result = for_each_remote(get_one_entry, &list);
 
        if (!result) {
                int i;
@@ -1295,6 +1327,7 @@ static int show_all(void)
                        }
                }
        }
+       string_list_clear(&list, 1);
        return result;
 }
 
@@ -1306,7 +1339,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)
diff --git a/builtin-replace.c b/builtin-replace.c
new file mode 100644 (file)
index 0000000..fe3a647
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Builtin "git replace"
+ *
+ * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
+ *
+ * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com>
+ * and Carlos Rica <jasampler@gmail.com> that was itself based on
+ * git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "refs.h"
+#include "parse-options.h"
+
+static const char * const git_replace_usage[] = {
+       "git replace [-f] <object> <replacement>",
+       "git replace -d <object>...",
+       "git replace -l [<pattern>]",
+       NULL
+};
+
+static int show_reference(const char *refname, const unsigned char *sha1,
+                         int flag, void *cb_data)
+{
+       const char *pattern = cb_data;
+
+       if (!fnmatch(pattern, refname, 0))
+               printf("%s\n", refname);
+
+       return 0;
+}
+
+static int list_replace_refs(const char *pattern)
+{
+       if (pattern == NULL)
+               pattern = "*";
+
+       for_each_replace_ref(show_reference, (void *) pattern);
+
+       return 0;
+}
+
+typedef int (*each_replace_name_fn)(const char *name, const char *ref,
+                                   const unsigned char *sha1);
+
+static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
+{
+       const char **p;
+       char ref[PATH_MAX];
+       int had_error = 0;
+       unsigned char sha1[20];
+
+       for (p = argv; *p; p++) {
+               if (snprintf(ref, sizeof(ref), "refs/replace/%s", *p)
+                                       >= sizeof(ref)) {
+                       error("replace ref name too long: %.*s...", 50, *p);
+                       had_error = 1;
+                       continue;
+               }
+               if (!resolve_ref(ref, sha1, 1, NULL)) {
+                       error("replace ref '%s' not found.", *p);
+                       had_error = 1;
+                       continue;
+               }
+               if (fn(*p, ref, sha1))
+                       had_error = 1;
+       }
+       return had_error;
+}
+
+static int delete_replace_ref(const char *name, const char *ref,
+                             const unsigned char *sha1)
+{
+       if (delete_ref(ref, sha1, 0))
+               return 1;
+       printf("Deleted replace ref '%s'\n", name);
+       return 0;
+}
+
+static int replace_object(const char *object_ref, const char *replace_ref,
+                         int force)
+{
+       unsigned char object[20], prev[20], repl[20];
+       char ref[PATH_MAX];
+       struct ref_lock *lock;
+
+       if (get_sha1(object_ref, object))
+               die("Failed to resolve '%s' as a valid ref.", object_ref);
+       if (get_sha1(replace_ref, repl))
+               die("Failed to resolve '%s' as a valid ref.", replace_ref);
+
+       if (snprintf(ref, sizeof(ref),
+                    "refs/replace/%s",
+                    sha1_to_hex(object)) > sizeof(ref) - 1)
+               die("replace ref name too long: %.*s...", 50, ref);
+       if (check_ref_format(ref))
+               die("'%s' is not a valid ref name.", ref);
+
+       if (!resolve_ref(ref, prev, 1, NULL))
+               hashclr(prev);
+       else if (!force)
+               die("replace ref '%s' already exists", ref);
+
+       lock = lock_any_ref_for_update(ref, prev, 0);
+       if (!lock)
+               die("%s: cannot lock the ref", ref);
+       if (write_ref_sha1(lock, repl, NULL) < 0)
+               die("%s: cannot update the ref", ref);
+
+       return 0;
+}
+
+int cmd_replace(int argc, const char **argv, const char *prefix)
+{
+       int list = 0, delete = 0, force = 0;
+       struct option options[] = {
+               OPT_BOOLEAN('l', NULL, &list, "list replace refs"),
+               OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"),
+               OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
+
+       if (list && delete)
+               usage_msg_opt("-l and -d cannot be used together",
+                             git_replace_usage, options);
+
+       if (force && (list || delete))
+               usage_msg_opt("-f cannot be used with -d or -l",
+                             git_replace_usage, options);
+
+       /* Delete refs */
+       if (delete) {
+               if (argc < 1)
+                       usage_msg_opt("-d needs at least one argument",
+                                     git_replace_usage, options);
+               return for_each_replace_name(argv, delete_replace_ref);
+       }
+
+       /* Replace object */
+       if (!list && argc) {
+               if (argc != 2)
+                       usage_msg_opt("bad number of arguments",
+                                     git_replace_usage, options);
+               return replace_object(argv[0], argv[1], force);
+       }
+
+       /* List refs, even if "list" is not set */
+       if (argc > 1)
+               usage_msg_opt("only one pattern can be given with -l",
+                             git_replace_usage, options);
+       if (force)
+               usage_msg_opt("-f needs some arguments",
+                             git_replace_usage, options);
+
+       return list_replace_refs(argv[0]);
+}
index 020af7377bb9aa1bced7489d40169e3cb2ff7682..adfb7b5f48597c19c23235b74cf1a0c779985dc6 100644 (file)
@@ -116,7 +116,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
                        if (!has_rerere_resolution(name))
                                unlink_rr_item(name);
                }
-               unlink(git_path("rr-cache/MERGE_RR"));
+               unlink_or_warn(git_path("rr-cache/MERGE_RR"));
        } else if (!strcmp(argv[1], "gc"))
                garbage_collect(&merge_rr);
        else if (!strcmp(argv[1], "status"))
index 7e7ebabaa865ebd749502774638efe7b6d26bec1..73e60223db844ee2445aff0b2253f0f21e8a5bef 100644 (file)
@@ -108,7 +108,8 @@ static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
        if (read_cache() < 0)
                return error("Could not read index");
 
-       result = refresh_cache(flags) ? 1 : 0;
+       result = refresh_index(&the_index, (flags), NULL, NULL,
+                              "Unstaged changes after reset:") ? 1 : 0;
        if (write_cache(fd, active_cache, active_nr) ||
                        commit_locked_index(index_lock))
                return error ("Could not refresh index");
@@ -142,6 +143,17 @@ static void update_index_from_diff(struct diff_queue_struct *q,
        }
 }
 
+static int interactive_reset(const char *revision, const char **argv,
+                            const char *prefix)
+{
+       const char **pathspec = NULL;
+
+       if (*argv)
+               pathspec = get_pathspec(prefix, argv);
+
+       return run_add_interactive(revision, "--patch=reset", pathspec);
+}
+
 static int read_from_tree(const char *prefix, const char **argv,
                unsigned char *tree_sha1, int refresh_flags)
 {
@@ -183,6 +195,7 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
        int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
+       int patch_mode = 0;
        const char *rev = "HEAD";
        unsigned char sha1[20], *orig = NULL, sha1_orig[20],
                                *old_orig = NULL, sha1_old_orig[20];
@@ -198,12 +211,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                "reset HEAD, index and working tree", MERGE),
                OPT_BOOLEAN('q', NULL, &quiet,
                                "disable showing new HEAD in hard reset and progress message"),
+               OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
                OPT_END()
        };
 
        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);
@@ -251,6 +265,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                die("Could not parse object '%s'.", rev);
        hashcpy(sha1, commit->object.sha1);
 
+       if (patch_mode) {
+               if (reset_type != NONE)
+                       die("--patch is incompatible with --{hard,mixed,soft}");
+               return interactive_reset(rev, argv + i, prefix);
+       }
+
        /* git reset tree [--] paths... can be used to
         * load chosen paths from the tree into the index without
         * affecting the working tree nor HEAD. */
@@ -261,7 +281,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                        die("Cannot do %s reset with paths.",
                                        reset_type_names[reset_type]);
                return read_from_tree(prefix, argv + i, sha1,
-                               quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
+                               quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
        }
        if (reset_type == NONE)
                reset_type = MIXED; /* by default */
@@ -302,7 +322,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                break;
        case MIXED: /* Report what has not been updated. */
                update_index_refresh(0, NULL,
-                               quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
+                               quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
                break;
        }
 
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..45bead65451e87a4b564e59fed3518a4de9198e9 100644 (file)
@@ -301,7 +301,7 @@ static const char *skipspaces(const char *s)
 
 static int cmd_parseopt(int argc, const char **argv, const char *prefix)
 {
-       static int keep_dashdash = 0;
+       static int keep_dashdash = 0, stop_at_non_option = 0;
        static char const * const parseopt_usage[] = {
                "git rev-parse --parseopt [options] -- [<args>...]",
                NULL
@@ -309,6 +309,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        static struct option parseopt_opts[] = {
                OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
                                        "keep the `--` passed as an arg"),
+               OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
+                                       "stop parsing after the "
+                                       "first non-option argument"),
                OPT_END(),
        };
 
@@ -318,7 +321,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,8 +396,9 @@ 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,
-                            keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0);
+       argc = parse_options(argc, argv, prefix, opts, usage,
+                       keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 |
+                       stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0);
 
        strbuf_addf(&parsed, " --");
        sq_quote_argv(&parsed, argv, 0);
@@ -402,6 +406,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 +435,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++) {
@@ -577,7 +596,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                        continue;
                                }
                                if (!getcwd(cwd, PATH_MAX))
-                                       die("unable to get current working directory");
+                                       die_errno("unable to get current working directory");
                                printf("%s/.git\n", cwd);
                                continue;
                        }
index 3f2614e1bbef009afadefff7d284225533db2a09..151aa6a981832954120359f7c953015525b530d8 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];
 
@@ -135,7 +135,7 @@ static void add_to_msg(const char *string)
 {
        int len = strlen(string);
        if (write_in_full(msg_fd, string, len) < 0)
-               die ("Could not write to MERGE_MSG");
+               die_errno ("Could not write to MERGE_MSG");
 }
 
 static void add_message_to_msg(const char *message)
@@ -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..57975dbcfd7c0dbcba03b88a1bf403f1ec5f528c 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);
 
@@ -256,7 +257,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!removed)
-                               die("git rm: %s: %s", path, strerror(errno));
+                               die_errno("git rm: '%s'", path);
                }
        }
 
index d5a1c48d0e2abab16c646ce86ac9670b95dddc07..37e528e28364fbde5a5ba31316aa7bf66d43586b 100644 (file)
@@ -43,19 +43,26 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
                "--stdout",
                NULL,
                NULL,
+               NULL,
+               NULL,
        };
        struct child_process po;
        int i;
 
+       i = 4;
        if (args->use_thin_pack)
-               argv[4] = "--thin";
+               argv[i++] = "--thin";
+       if (args->use_ofs_delta)
+               argv[i++] = "--delta-base-offset";
+       if (args->quiet)
+               argv[i++] = "-q";
        memset(&po, 0, sizeof(po));
        po.argv = argv;
        po.in = -1;
        po.out = fd;
        po.git_cmd = 1;
        if (start_command(&po))
-               die("git pack-objects failed (%s)", strerror(errno));
+               die_errno("git pack-objects failed");
 
        /*
         * We feed the pack-objects we just spawned with revision
@@ -174,9 +181,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);
@@ -315,6 +322,8 @@ int send_pack(struct send_pack_args *args,
                ask_for_status_report = 1;
        if (server_supports("delete-refs"))
                allow_deleting_refs = 1;
+       if (server_supports("ofs-delta"))
+               args->use_ofs_delta = 1;
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -467,7 +476,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";
@@ -561,13 +570,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..4d4a3c82d6209ce3ec897dad0c69d58a329b4303 100644 (file)
@@ -56,7 +56,7 @@ static void insert_one_record(struct shortlog *log,
        /* copy author name to namebuf, to support matching on both name and email */
        memcpy(namebuf, author, boemail - author);
        len = boemail - author;
-       while(len > 0 && isspace(namebuf[len-1]))
+       while (len > 0 && isspace(namebuf[len-1]))
                len--;
        namebuf[len] = 0;
 
@@ -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 828e6f86de22f8def68a49b702a27521c248a012..be95930b783f47ade274e281227844e795729f5a 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 [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...",
+    "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]",
+    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) {
@@ -536,7 +565,15 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "showbranch.default")) {
                if (!value)
                        return config_error_nonbool(var);
-               if (default_alloc <= default_num + 1) {
+               /*
+                * default_arg is now passed to parse_options(), so we need to
+                * mimick the real argv a bit better.
+                */
+               if (!default_num) {
+                       default_alloc = 20;
+                       default_arg = xcalloc(default_alloc, sizeof(*default_arg));
+                       default_arg[default_num++] = "show-branch";
+               } else if (default_alloc <= default_num + 1) {
                        default_alloc = default_alloc * 3 / 2 + 20;
                        default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);
                }
@@ -545,7 +582,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 +611,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 + 9);
+               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 +655,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,
+                           "show possible merge bases"),
+               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] */
+               ac = default_num;
+               av = default_arg;
        }
 
-       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 +723,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 +890,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 +952,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 dc76c5090f2367426201b37c5b13c2a5cbf00de2..c46550c9c01da8fe74145e16656a5163a7e4f07b 100644 (file)
@@ -4,12 +4,18 @@
 #include "object.h"
 #include "tag.h"
 #include "string-list.h"
+#include "parse-options.h"
 
-static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] < ref-list";
+static const char * const show_ref_usage[] = {
+       "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ",
+       "git show-ref --exclude-existing[=pattern] < ref-list",
+       NULL
+};
 
-static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
-       found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
+static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
+          quiet, hash_only, abbrev, exclude_arg;
 static const char **pattern;
+static const char *exclude_existing_arg;
 
 static void show_one(const char *refname, const unsigned char *sha1)
 {
@@ -150,79 +156,60 @@ static int exclude_existing(const char *match)
        return 0;
 }
 
+static int hash_callback(const struct option *opt, const char *arg, int unset)
+{
+       hash_only = 1;
+       /* Use full length SHA1 if no argument */
+       if (!arg)
+               return 0;
+       return parse_opt_abbrev_cb(opt, arg, unset);
+}
+
+static int exclude_existing_callback(const struct option *opt, const char *arg,
+                                    int unset)
+{
+       exclude_arg = 1;
+       *(const char **)opt->value = arg;
+       return 0;
+}
+
+static int help_callback(const struct option *opt, const char *arg, int unset)
+{
+       return -1;
+}
+
+static const struct option show_ref_options[] = {
+       OPT_BOOLEAN(0, "tags", &tags_only, "only show tags (can be combined with heads)"),
+       OPT_BOOLEAN(0, "heads", &heads_only, "only show heads (can be combined with tags)"),
+       OPT_BOOLEAN(0, "verify", &verify, "stricter reference checking, "
+                   "requires exact ref path"),
+       OPT_BOOLEAN('h', "head", &show_head, "show the HEAD reference"),
+       OPT_BOOLEAN('d', "dereference", &deref_tags,
+                   "dereference tags into object IDs"),
+       { OPTION_CALLBACK, 's', "hash", &abbrev, "n",
+         "only show SHA1 hash using <n> digits",
+         PARSE_OPT_OPTARG, &hash_callback },
+       OPT__ABBREV(&abbrev),
+       OPT__QUIET(&quiet),
+       { OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
+         "pattern", "show refs from stdin that aren't in local repository",
+         PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
+       { OPTION_CALLBACK, 0, "help-all", NULL, NULL, "show usage",
+         PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
+       OPT_END()
+};
+
 int cmd_show_ref(int argc, const char **argv, const char *prefix)
 {
-       int i;
+       argc = parse_options(argc, argv, prefix, show_ref_options,
+                            show_ref_usage, PARSE_OPT_NO_INTERNAL_HELP);
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (*arg != '-') {
-                       pattern = argv + i;
-                       break;
-               }
-               if (!strcmp(arg, "--")) {
-                       pattern = argv + i + 1;
-                       if (!*pattern)
-                               pattern = NULL;
-                       break;
-               }
-               if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) {
-                       quiet = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-h") || !strcmp(arg, "--head")) {
-                       show_head = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-d") || !strcmp(arg, "--dereference")) {
-                       deref_tags = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-s") || !strcmp(arg, "--hash")) {
-                       hash_only = 1;
-                       continue;
-               }
-               if (!prefixcmp(arg, "--hash=") ||
-                   (!prefixcmp(arg, "--abbrev") &&
-                    (arg[8] == '=' || arg[8] == '\0'))) {
-                       if (arg[2] != 'h' && !arg[8])
-                               /* --abbrev only */
-                               abbrev = DEFAULT_ABBREV;
-                       else {
-                               /* --hash= or --abbrev= */
-                               char *end;
-                               if (arg[2] == 'h') {
-                                       hash_only = 1;
-                                       arg += 7;
-                               }
-                               else
-                                       arg += 9;
-                               abbrev = strtoul(arg, &end, 10);
-                               if (*end || abbrev > 40)
-                                       usage(show_ref_usage);
-                               if (abbrev < MINIMUM_ABBREV)
-                                       abbrev = MINIMUM_ABBREV;
-                       }
-                       continue;
-               }
-               if (!strcmp(arg, "--verify")) {
-                       verify = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--tags")) {
-                       tags_only = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--heads")) {
-                       heads_only = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--exclude-existing"))
-                       return exclude_existing(NULL);
-               if (!prefixcmp(arg, "--exclude-existing="))
-                       return exclude_existing(arg + 19);
-               usage(show_ref_usage);
-       }
+       if (exclude_arg)
+               return exclude_existing(exclude_existing_arg);
+
+       pattern = argv;
+       if (!*pattern)
+               pattern = NULL;
 
        if (verify) {
                if (!pattern)
index d6e3896c006796ccca12c00de45e36583387f05b..1fd2205d530ad510a18ff5a441ca7b5cb1cc34a6 100644 (file)
@@ -78,7 +78,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
                strip_comments = 1;
 
        if (strbuf_read(&buf, 0, 1024) < 0)
-               die("could not read the input");
+               die_errno("could not read the input");
 
        stripspace(&buf, strip_comments);
 
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 01e73747d02f384c5e31b846340a4b586c84aab3..c4790185ebb9979a6772cf7159d16c2a1deeeaf3 100644 (file)
@@ -308,8 +308,7 @@ static void create_tag(const unsigned char *object, const char *tag,
                path = git_pathdup("TAG_EDITMSG");
                fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
                if (fd < 0)
-                       die("could not create file '%s': %s",
-                                               path, strerror(errno));
+                       die_errno("could not create file '%s'", path);
 
                if (!is_null_sha1(prev))
                        write_tag_body(fd, prev);
@@ -338,7 +337,7 @@ static void create_tag(const unsigned char *object, const char *tag,
                exit(128);
        }
        if (path) {
-               unlink(path);
+               unlink_or_warn(path);
                free(path);
        }
 }
@@ -376,8 +375,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,11 +386,11 @@ 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"),
-               OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
+               OPT_BOOLEAN('f', "force", &force, "replace the tag if exists"),
 
                OPT_GROUP("Tag listing options"),
                {
@@ -405,8 +404,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;
@@ -444,11 +442,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                else {
                        if (!strcmp(msgfile, "-")) {
                                if (strbuf_read(&buf, 0, 1024) < 0)
-                                       die("cannot read %s", msgfile);
+                                       die_errno("cannot read '%s'", msgfile);
                        } else {
                                if (strbuf_read_file(&buf, msgfile, 1024) < 0)
-                                       die("could not open or read '%s': %s",
-                                               msgfile, strerror(errno));
+                                       die_errno("could not open or read '%s'",
+                                               msgfile);
                        }
                }
        }
index f88e7219367c309dbc3d3e7ce2db32666703a91d..8b3a35e12da2cca8b632d4139bc2c5ba2409d322 100644 (file)
@@ -91,7 +91,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
 
        n = write_in_full(1, content + 11, 41);
        if (n < 41)
-               die("git get-tar-commit-id: write error");
+               die_errno("git get-tar-commit-id: write error");
 
        return 0;
 }
index 9a773239cabab9998bcea829c0fb2abea9bdb8e8..685566e0b5e458c510fdf989744d63dda29e28f0 100644 (file)
@@ -68,7 +68,7 @@ static void *fill(int min)
                if (ret <= 0) {
                        if (!ret)
                                die("early EOF");
-                       die("read error on input: %s", strerror(errno));
+                       die_errno("read error on input");
                }
                len += ret;
        } while (len < min);
@@ -158,7 +158,7 @@ struct obj_info {
 #define FLAG_WRITTEN (1u<<21)
 
 static struct obj_info *obj_list;
-unsigned nr_objects;
+static unsigned nr_objects;
 
 /*
  * Called only from check_object() after it verified this object
@@ -181,10 +181,10 @@ static void write_cached_object(struct object *obj)
 static int check_object(struct object *obj, int type, void *data)
 {
        if (!obj)
-               return 0;
+               return 1;
 
        if (obj->flags & FLAG_WRITTEN)
-               return 1;
+               return 0;
 
        if (type != OBJ_ANY && obj->type != type)
                die("object type mismatch");
@@ -195,22 +195,24 @@ static int check_object(struct object *obj, int type, void *data)
                if (type != obj->type || type <= 0)
                        die("object of unexpected type");
                obj->flags |= FLAG_WRITTEN;
-               return 1;
+               return 0;
        }
 
        if (fsck_object(obj, 1, fsck_error_function))
                die("Error in object");
-       if (!fsck_walk(obj, check_object, 0))
+       if (fsck_walk(obj, check_object, NULL))
                die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
        write_cached_object(obj);
-       return 1;
+       return 0;
 }
 
 static void write_rest(void)
 {
        unsigned i;
-       for (i = 0; i < nr_objects; i++)
-               check_object(obj_list[i].obj, OBJ_ANY, 0);
+       for (i = 0; i < nr_objects; i++) {
+               if (obj_list[i].obj)
+                       check_object(obj_list[i].obj, OBJ_ANY, NULL);
+       }
 }
 
 static void added_object(unsigned nr, enum object_type type,
@@ -422,8 +424,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;
@@ -495,6 +497,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
        int i;
        unsigned char sha1[20];
 
+       read_replace_refs = 0;
+
        git_config(git_default_config, NULL);
 
        quiet = !isatty(2);
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.");
 
diff --git a/builtin-update-server-info.c b/builtin-update-server-info.c
new file mode 100644 (file)
index 0000000..2b3fddc
--- /dev/null
@@ -0,0 +1,25 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static const char * const update_server_info_usage[] = {
+       "git update-server-info [--force]",
+       NULL
+};
+
+int cmd_update_server_info(int argc, const char **argv, const char *prefix)
+{
+       int force = 0;
+       struct option options[] = {
+               OPT_BOOLEAN('f', "force", &force,
+                       "update the info files from scratch"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            update_server_info_usage, 0);
+       if (argc > 0)
+               usage_with_options(update_server_info_usage, options);
+
+       return !!update_server_info(force);
+}
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 0ee0a9af60b0601fe0e6db98ec582e059f5e9064..b6079ae6cb03c7f3112c6eebc8c9a012d690a125 100644 (file)
@@ -2,15 +2,23 @@
 #include "cache.h"
 #include "pack.h"
 #include "pack-revindex.h"
+#include "parse-options.h"
 
 #define MAX_CHAIN 50
 
-static void show_pack_info(struct packed_git *p)
+#define VERIFY_PACK_VERBOSE 01
+#define VERIFY_PACK_STAT_ONLY 02
+
+static void show_pack_info(struct packed_git *p, unsigned int flags)
 {
-       uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1];
+       uint32_t nr_objects, i;
+       int cnt;
+       int stat_only = flags & VERIFY_PACK_STAT_ONLY;
+       unsigned long chain_histogram[MAX_CHAIN+1], baseobjects;
 
        nr_objects = p->num_objects;
        memset(chain_histogram, 0, sizeof(chain_histogram));
+       baseobjects = 0;
 
        for (i = 0; i < nr_objects; i++) {
                const unsigned char *sha1;
@@ -28,14 +36,19 @@ static void show_pack_info(struct packed_git *p)
                type = packed_object_info_detail(p, offset, &size, &store_size,
                                                 &delta_chain_length,
                                                 base_sha1);
-               printf("%s ", sha1_to_hex(sha1));
-               if (!delta_chain_length)
-                       printf("%-6s %lu %lu %"PRIuMAX"\n",
-                              type, size, store_size, (uintmax_t)offset);
+               if (!stat_only)
+                       printf("%s ", sha1_to_hex(sha1));
+               if (!delta_chain_length) {
+                       if (!stat_only)
+                               printf("%-6s %lu %lu %"PRIuMAX"\n",
+                                      type, size, store_size, (uintmax_t)offset);
+                       baseobjects++;
+               }
                else {
-                       printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
-                              type, size, store_size, (uintmax_t)offset,
-                              delta_chain_length, sha1_to_hex(base_sha1));
+                       if (!stat_only)
+                               printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
+                                      type, size, store_size, (uintmax_t)offset,
+                                      delta_chain_length, sha1_to_hex(base_sha1));
                        if (delta_chain_length <= MAX_CHAIN)
                                chain_histogram[delta_chain_length]++;
                        else
@@ -43,21 +56,29 @@ static void show_pack_info(struct packed_git *p)
                }
        }
 
-       for (i = 0; i <= MAX_CHAIN; i++) {
-               if (!chain_histogram[i])
+       if (baseobjects)
+               printf("non delta: %lu object%s\n",
+                      baseobjects, baseobjects > 1 ? "s" : "");
+
+       for (cnt = 1; cnt <= MAX_CHAIN; cnt++) {
+               if (!chain_histogram[cnt])
                        continue;
-               printf("chain length = %"PRIu32": %"PRIu32" object%s\n", i,
-                      chain_histogram[i], chain_histogram[i] > 1 ? "s" : "");
+               printf("chain length = %d: %lu object%s\n", cnt,
+                      chain_histogram[cnt],
+                      chain_histogram[cnt] > 1 ? "s" : "");
        }
        if (chain_histogram[0])
-               printf("chain length > %d: %"PRIu32" object%s\n", MAX_CHAIN,
-                      chain_histogram[0], chain_histogram[0] > 1 ? "s" : "");
+               printf("chain length > %d: %lu object%s\n", MAX_CHAIN,
+                      chain_histogram[0],
+                      chain_histogram[0] > 1 ? "s" : "");
 }
 
-static int verify_one_pack(const char *path, int verbose)
+static int verify_one_pack(const char *path, unsigned int flags)
 {
        char arg[PATH_MAX];
        int len;
+       int verbose = flags & VERIFY_PACK_VERBOSE;
+       int stat_only = flags & VERIFY_PACK_STAT_ONLY;
        struct packed_git *pack;
        int err;
 
@@ -93,50 +114,53 @@ static int verify_one_pack(const char *path, int verbose)
                return error("packfile %s not found.", arg);
 
        install_packed_git(pack);
-       err = verify_pack(pack);
 
-       if (verbose) {
+       if (!stat_only)
+               err = verify_pack(pack);
+       else
+               err = open_pack_index(pack);
+
+       if (verbose || stat_only) {
                if (err)
                        printf("%s: bad\n", pack->pack_name);
                else {
-                       show_pack_info(pack);
-                       printf("%s: ok\n", pack->pack_name);
+                       show_pack_info(pack, flags);
+                       if (!stat_only)
+                               printf("%s: ok\n", pack->pack_name);
                }
        }
 
        return err;
 }
 
-static const char verify_pack_usage[] = "git verify-pack [-v] <pack>...";
+static const char * const verify_pack_usage[] = {
+       "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...",
+       NULL
+};
 
 int cmd_verify_pack(int argc, const char **argv, const char *prefix)
 {
        int err = 0;
-       int verbose = 0;
-       int no_more_options = 0;
-       int nothing_done = 1;
+       unsigned int flags = 0;
+       int i;
+       const struct option verify_pack_options[] = {
+               OPT_BIT('v', "verbose", &flags, "verbose",
+                       VERIFY_PACK_VERBOSE),
+               OPT_BIT('s', "stat-only", &flags, "show statistics only",
+                       VERIFY_PACK_STAT_ONLY),
+               OPT_END()
+       };
 
        git_config(git_default_config, NULL);
-       while (1 < argc) {
-               if (!no_more_options && argv[1][0] == '-') {
-                       if (!strcmp("-v", argv[1]))
-                               verbose = 1;
-                       else if (!strcmp("--", argv[1]))
-                               no_more_options = 1;
-                       else
-                               usage(verify_pack_usage);
-               }
-               else {
-                       if (verify_one_pack(argv[1], verbose))
-                               err = 1;
-                       discard_revindex();
-                       nothing_done = 0;
-               }
-               argc--; argv++;
+       argc = parse_options(argc, argv, prefix, verify_pack_options,
+                            verify_pack_usage, 0);
+       if (argc < 1)
+               usage_with_options(verify_pack_usage, verify_pack_options);
+       for (i = 0; i < argc; i++) {
+               if (verify_one_pack(argv[i], flags))
+                       err = 1;
+               discard_revindex();
        }
 
-       if (nothing_done)
-               usage(verify_pack_usage);
-
        return err;
 }
index 729a1593e61d87ad4596f07e7faedac81de64e81..9f482c29f516bde84023f401b28b133c1e605333 100644 (file)
 #include "tag.h"
 #include "run-command.h"
 #include <signal.h>
+#include "parse-options.h"
 
-static const char builtin_verify_tag_usage[] =
-               "git verify-tag [-v|--verbose] <tag>...";
+static const char * const verify_tag_usage[] = {
+               "git verify-tag [-v|--verbose] <tag>...",
+               NULL
+};
 
 #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
 
@@ -55,7 +58,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
        close(gpg.in);
        ret = finish_command(&gpg);
 
-       unlink(path);
+       unlink_or_warn(path);
 
        return ret;
 }
@@ -89,17 +92,17 @@ static int verify_tag(const char *name, int verbose)
 int cmd_verify_tag(int argc, const char **argv, const char *prefix)
 {
        int i = 1, verbose = 0, had_error = 0;
+       const struct option verify_tag_options[] = {
+               OPT__VERBOSE(&verbose),
+               OPT_END()
+       };
 
        git_config(git_default_config, NULL);
 
-       if (argc > 1 &&
-           (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))) {
-               verbose = 1;
-               i++;
-       }
-
+       argc = parse_options(argc, argv, prefix, verify_tag_options,
+                            verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
        if (argc <= i)
-               usage(builtin_verify_tag_usage);
+               usage_with_options(verify_tag_usage, verify_tag_options);
 
        /* sometimes the program was terminated because this signal
         * was received in the process of writing the gpg input: */
index 9d640508dd8eb62201b286490b7f83486470d611..b223af416fee5fc219fbcca7afa2b9e03feaa7d0 100644 (file)
@@ -7,33 +7,37 @@
 #include "cache.h"
 #include "tree.h"
 #include "cache-tree.h"
+#include "parse-options.h"
 
-static const char write_tree_usage[] =
-"git write-tree [--missing-ok] [--prefix=<prefix>/]";
+static const char * const write_tree_usage[] = {
+       "git write-tree [--missing-ok] [--prefix=<prefix>/]",
+       NULL
+};
 
 int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
 {
-       int missing_ok = 0, ret;
+       int flags = 0, ret;
        const char *prefix = NULL;
        unsigned char sha1[20];
        const char *me = "git-write-tree";
+       struct option write_tree_options[] = {
+               OPT_BIT(0, "missing-ok", &flags, "allow missing objects",
+                       WRITE_TREE_MISSING_OK),
+               { OPTION_STRING, 0, "prefix", &prefix, "<prefix>/",
+                 "write tree object for a subdirectory <prefix>" ,
+                 PARSE_OPT_LITERAL_ARGHELP },
+               { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL,
+                 "only useful for debugging",
+                 PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL,
+                 WRITE_TREE_IGNORE_CACHE_TREE },
+               OPT_END()
+       };
 
        git_config(git_default_config, NULL);
-       while (1 < argc) {
-               const char *arg = argv[1];
-               if (!strcmp(arg, "--missing-ok"))
-                       missing_ok = 1;
-               else if (!prefixcmp(arg, "--prefix="))
-                       prefix = arg + 9;
-               else
-                       usage(write_tree_usage);
-               argc--; argv++;
-       }
-
-       if (argc > 2)
-               die("too many options");
+       argc = parse_options(argc, argv, unused_prefix, write_tree_options,
+                            write_tree_usage, 0);
 
-       ret = write_cache_as_tree(sha1, missing_ok, prefix);
+       ret = write_cache_as_tree(sha1, flags, prefix);
        switch (ret) {
        case 0:
                printf("%s\n", sha1_to_hex(sha1));
index 425ff8e89b361c34b3336cda58794682c66b57f3..a2174dc855ad89e486c57ae1c3a458cb1572edbb 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -13,7 +13,6 @@ extern const char git_more_info_string[];
 extern void list_common_cmds_help(void);
 extern const char *help_unknown_cmd(const char *cmd);
 extern void prune_packed_objects(int);
-extern int read_line_with_nul(char *buf, int size, FILE *file);
 extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
        struct strbuf *out);
 extern int commit_tree(const char *msg, unsigned char *tree,
@@ -72,6 +71,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);
@@ -102,6 +102,7 @@ extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_update_index(int argc, const char **argv, const char *prefix);
 extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
@@ -111,5 +112,6 @@ extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
+extern int cmd_replace(int argc, const char **argv, const char *prefix);
 
 #endif
index d0dd818b31faa04caa4d418a39ad3020a919aa2d..df95e151e2e82b48e4f15f2180838bf2d178f923 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;
@@ -234,7 +234,7 @@ int create_bundle(struct bundle_header *header, const char *path,
        rls.git_cmd = 1;
        if (start_command(&rls))
                return -1;
-       rls_fout = fdopen(rls.out, "r");
+       rls_fout = xfdopen(rls.out, "r");
        while (fgets(buffer, sizeof(buffer), rls_fout)) {
                unsigned char sha1[20];
                if (buffer[0] == '-') {
index 37bf35e636f0966662416f0693dbea5c1cc73311..d91743775dfbe98d99d8ab25a270af320c8b984e 100644 (file)
@@ -329,7 +329,8 @@ static int update_one(struct cache_tree *it,
                        entlen = pathlen - baselen;
                }
                if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
-                       return error("invalid object %s", sha1_to_hex(sha1));
+                       return error("invalid object %06o %s for '%.*s'",
+                               mode, sha1_to_hex(sha1), entlen+baselen, path);
 
                if (ce->ce_flags & CE_REMOVE)
                        continue; /* entry being removed */
@@ -514,6 +515,8 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
 
 static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
 {
+       if (!it)
+               return NULL;
        while (*path) {
                const char *slash;
                struct cache_tree_sub *sub;
@@ -538,28 +541,32 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
        return it;
 }
 
-int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix)
+int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
 {
        int entries, was_valid, newfd;
+       struct lock_file *lock_file;
 
        /*
         * We can't free this memory, it becomes part of a linked list
         * parsed atexit()
         */
-       struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+       lock_file = xcalloc(1, sizeof(struct lock_file));
 
        newfd = hold_locked_index(lock_file, 1);
 
        entries = read_cache();
        if (entries < 0)
                return WRITE_TREE_UNREADABLE_INDEX;
+       if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
+               cache_tree_free(&(active_cache_tree));
 
        if (!active_cache_tree)
                active_cache_tree = cache_tree();
 
        was_valid = cache_tree_fully_valid(active_cache_tree);
-
        if (!was_valid) {
+               int missing_ok = flags & WRITE_TREE_MISSING_OK;
+
                if (cache_tree_update(active_cache_tree,
                                      active_cache, active_nr,
                                      missing_ok, 0) < 0)
@@ -625,3 +632,35 @@ void prime_cache_tree(struct cache_tree **it, struct tree *tree)
        *it = cache_tree();
        prime_cache_tree_rec(*it, tree);
 }
+
+/*
+ * find the cache_tree that corresponds to the current level without
+ * exploding the full path into textual form.  The root of the
+ * cache tree is given as "root", and our current level is "info".
+ * (1) When at root level, info->prev is NULL, so it is "root" itself.
+ * (2) Otherwise, find the cache_tree that corresponds to one level
+ *     above us, and find ourselves in there.
+ */
+static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root,
+                                                        struct traverse_info *info)
+{
+       struct cache_tree *our_parent;
+
+       if (!info->prev)
+               return root;
+       our_parent = find_cache_tree_from_traversal(root, info->prev);
+       return cache_tree_find(our_parent, info->name.path);
+}
+
+int cache_tree_matches_traversal(struct cache_tree *root,
+                                struct name_entry *ent,
+                                struct traverse_info *info)
+{
+       struct cache_tree *it;
+
+       it = find_cache_tree_from_traversal(root, info);
+       it = cache_tree_find(it, ent->path);
+       if (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1))
+               return it->entry_count;
+       return 0;
+}
index e95883523633a51f833683e96af1738da2868238..3df641f59311f43aa951a2cdfa9f110b97b13a45 100644 (file)
@@ -2,6 +2,7 @@
 #define CACHE_TREE_H
 
 #include "tree.h"
+#include "tree-walk.h"
 
 struct cache_tree;
 struct cache_tree_sub {
@@ -30,11 +31,18 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
 int cache_tree_fully_valid(struct cache_tree *);
 int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);
 
+/* bitmasks to write_cache_as_tree flags */
+#define WRITE_TREE_MISSING_OK 1
+#define WRITE_TREE_IGNORE_CACHE_TREE 2
+
+/* error return codes */
 #define WRITE_TREE_UNREADABLE_INDEX (-1)
 #define WRITE_TREE_UNMERGED_INDEX (-2)
 #define WRITE_TREE_PREFIX_ERROR (-3)
 
-int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix);
+int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);
 void prime_cache_tree(struct cache_tree **, struct tree *);
 
+extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
+
 #endif
diff --git a/cache.h b/cache.h
index b8503ad91c3b13ccaf87a6f596d13918f7ae114b..96840c7af78aac3c760586dd8018652ec9ddefed 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -4,6 +4,7 @@
 #include "git-compat-util.h"
 #include "strbuf.h"
 #include "hash.h"
+#include "advice.h"
 
 #include SHA1_HEADER
 #ifndef git_SHA_CTX
@@ -330,7 +331,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
 #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
 #define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
 #define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
-#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
+#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
 #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
@@ -468,13 +469,16 @@ extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_obje
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
+/* "careful lstat()" */
+extern int check_path(const char *path, int len, struct stat *st, int skiplen);
+
 #define REFRESH_REALLY         0x0001  /* ignore_valid */
 #define REFRESH_UNMERGED       0x0002  /* allow unmerged */
 #define REFRESH_QUIET          0x0004  /* be quiet about it */
 #define REFRESH_IGNORE_MISSING 0x0008  /* ignore non-existent */
 #define REFRESH_IGNORE_SUBMODULES      0x0010  /* ignore submodules */
-#define REFRESH_SAY_CHANGED    0x0020  /* say "changed" not "needs update" */
-extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
+#define REFRESH_IN_PORCELAIN   0x0020  /* user friendly output, not "needs update" */
+extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg);
 
 struct lock_file {
        struct lock_file *next;
@@ -485,6 +489,7 @@ struct lock_file {
 };
 #define LOCK_DIE_ON_ERROR 1
 #define LOCK_NODEREF 2
+extern int unable_to_lock_error(const char *path, int err);
 extern NORETURN void unable_to_lock_index_die(const char *path, int err);
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
@@ -509,6 +514,7 @@ extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
+extern const char *apply_default_ignorewhitespace;
 extern int zlib_compression_level;
 extern int core_compression_level;
 extern int core_compression_seen;
@@ -516,6 +522,7 @@ extern size_t packed_git_window_size;
 extern size_t packed_git_limit;
 extern size_t delta_base_cache_limit;
 extern int auto_crlf;
+extern int read_replace_refs;
 extern int fsync_object_files;
 extern int core_preload_index;
 
@@ -543,7 +550,6 @@ enum rebase_setup_type {
 };
 
 enum push_default_type {
-       PUSH_DEFAULT_UNSPECIFIED = -1,
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
        PUSH_DEFAULT_TRACKING,
@@ -561,6 +567,8 @@ enum object_creation_mode {
 
 extern enum object_creation_mode object_creation_mode;
 
+extern int grafts_replace_parents;
+
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
 extern int check_repository_format(void);
@@ -614,6 +622,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!!
  *
@@ -649,7 +659,11 @@ char *strip_path_suffix(const char *path, const char *suffix);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
-extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
+extern void *read_sha1_file_repl(const unsigned char *sha1, enum object_type *type, unsigned long *size, const unsigned char **replacement);
+static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+{
+       return read_sha1_file_repl(sha1, type, size, NULL);
+}
 extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
@@ -719,9 +733,14 @@ enum date_mode {
 };
 
 const char *show_date(unsigned long time, int timezone, enum date_mode mode);
+const char *show_date_relative(unsigned long time, int tz,
+                              const struct timeval *now,
+                              char *timebuf,
+                              size_t timebuf_size);
 int parse_date(const char *date, char *buf, int bufsize);
 void datestamp(char *buf, int bufsize);
 unsigned long approxidate(const char *);
+unsigned long approxidate_relative(const char *date, const struct timeval *now);
 enum date_mode parse_date_format(const char *format);
 
 #define IDENT_WARN_ON_NO_NAME  1
@@ -742,7 +761,17 @@ struct checkout {
 };
 
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
+
+struct cache_def {
+       char path[PATH_MAX + 1];
+       int len;
+       int flags;
+       int track_flags;
+       int prefix_len_stat_func;
+};
+
 extern int has_symlink_leading_path(const char *name, int len);
+extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 extern int has_symlink_or_noent_leading_path(const char *name, int len);
 extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 extern void invalidate_lstat_cache(const char *name, int len);
@@ -896,13 +925,19 @@ extern const char *git_mailmap_file;
 extern void maybe_flush_or_die(FILE *, const char *);
 extern int copy_fd(int ifd, int ofd);
 extern int copy_file(const char *dst, const char *src, int mode);
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
-extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+extern int copy_file_with_time(const char *dst, const char *src, int mode);
 extern void write_or_die(int fd, const void *buf, size_t count);
 extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
 extern void fsync_or_die(int fd, const char *);
 
+extern ssize_t read_in_full(int fd, void *buf, size_t count);
+extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+static inline ssize_t write_str_in_full(int fd, const char *str)
+{
+       return write_in_full(fd, str, strlen(str));
+}
+
 /* pager.c */
 extern void setup_pager(void);
 extern const char *pager_program;
@@ -951,10 +986,12 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i
  * whitespace rules.
  * used by both diff and apply
  */
-#define WS_TRAILING_SPACE      01
+#define WS_BLANK_AT_EOL         01
 #define WS_SPACE_BEFORE_TAB    02
 #define WS_INDENT_WITH_NON_TAB 04
 #define WS_CR_AT_EOL           010
+#define WS_BLANK_AT_EOF        020
+#define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 extern unsigned whitespace_rule_cfg;
 extern unsigned whitespace_rule(const char *);
index 60d03676bbd0dba7be79c7098d7cae553962ca98..5b63af1eeb71907f23117bc552fa7a4019f09722 100644 (file)
@@ -80,6 +80,7 @@ struct lline {
 /* Lines surviving in the merge result */
 struct sline {
        struct lline *lost_head, **lost_tail;
+       struct lline *next_lost;
        char *bol;
        int len;
        /* bit 0 up to (N-1) are on if the parent has this line (i.e.
@@ -121,18 +122,12 @@ static void append_lost(struct sline *sline, int n, const char *line, int len)
 
        /* Check to see if we can squash things */
        if (sline->lost_head) {
-               struct lline *last_one = NULL;
-               /* We cannot squash it with earlier one */
-               for (lline = sline->lost_head;
-                    lline;
-                    lline = lline->next)
-                       if (lline->parent_map & this_mask)
-                               last_one = lline;
-               lline = last_one ? last_one->next : sline->lost_head;
+               lline = sline->next_lost;
                while (lline) {
                        if (lline->len == len &&
                            !memcmp(lline->line, line, len)) {
                                lline->parent_map |= this_mask;
+                               sline->next_lost = lline->next;
                                return;
                        }
                        lline = lline->next;
@@ -147,6 +142,7 @@ static void append_lost(struct sline *sline, int n, const char *line, int len)
        lline->line[len] = 0;
        *sline->lost_tail = lline;
        sline->lost_tail = &lline->next;
+       sline->next_lost = NULL;
 }
 
 struct combine_diff_state {
@@ -168,25 +164,28 @@ static void consume_line(void *state_, char *line, unsigned long len)
                                      &state->nb, &state->nn))
                        return;
                state->lno = state->nb;
-               if (!state->nb)
-                       /* @@ -1,2 +0,0 @@ to remove the
-                        * first two lines...
-                        */
-                       state->nb = 1;
-               if (state->nn == 0)
+               if (state->nn == 0) {
                        /* @@ -X,Y +N,0 @@ removed Y lines
                         * that would have come *after* line N
                         * in the result.  Our lost buckets hang
                         * to the line after the removed lines,
+                        *
+                        * Note that this is correct even when N == 0,
+                        * in which case the hunk removes the first
+                        * line in the file.
                         */
                        state->lost_bucket = &state->sline[state->nb];
-               else
+                       if (!state->nb)
+                               state->nb = 1;
+               } else {
                        state->lost_bucket = &state->sline[state->nb-1];
+               }
                if (!state->sline[state->nb-1].p_lno)
                        state->sline[state->nb-1].p_lno =
                                xcalloc(state->num_parent,
                                        sizeof(unsigned long));
                state->sline[state->nb-1].p_lno[state->n] = state->ob;
+               state->lost_bucket->next_lost = state->lost_bucket->lost_head;
                return;
        }
        if (!state->lost_bucket)
@@ -746,7 +745,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
 
                        done = read_in_full(fd, result, len);
                        if (done < 0)
-                               die("read error '%s'", elem->path);
+                               die_errno("read error '%s'", elem->path);
                        else if (done < len)
                                die("early EOF '%s'", elem->path);
 
index fb03a2ebb5d51f46d00fad3b3f6b1794d4fdad2b..59b0adc39b7c2d30103fe5d30b416b6d4ca64c31 100644 (file)
@@ -92,6 +92,7 @@ git-reflog                              ancillarymanipulators
 git-relink                              ancillarymanipulators
 git-remote                              ancillarymanipulators
 git-repack                              ancillarymanipulators
+git-replace                             ancillarymanipulators
 git-repo-config                         ancillarymanipulators  deprecated
 git-request-pull                        foreignscminterface
 git-rerere                              ancillaryinterrogators
index aa3b35b6a86891ac9d0628e20a6a46d506bf7700..fedbd5e5267ec4beaf1f245a879918a2685c10b9 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -50,7 +50,6 @@ struct commit *lookup_commit(const unsigned char *sha1)
 
 static unsigned long parse_commit_date(const char *buf, const char *tail)
 {
-       unsigned long date;
        const char *dateptr;
 
        if (buf + 6 >= tail)
@@ -73,10 +72,7 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
        if (buf >= tail)
                return 0;
        /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-       date = strtoul(dateptr, NULL, 10);
-       if (date == ULONG_MAX)
-               date = 0;
-       return date;
+       return strtoul(dateptr, NULL, 10);
 }
 
 static struct commit_graft **commit_graft;
@@ -216,7 +212,7 @@ int write_shallow_commits(int fd, int use_pack_protocol)
                        else {
                                if (write_in_full(fd, hex,  40) != 40)
                                        break;
-                               if (write_in_full(fd, "\n", 1) != 1)
+                               if (write_str_in_full(fd, "\n") != 1)
                                        break;
                        }
                }
@@ -266,7 +262,11 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
                    bufptr[47] != '\n')
                        return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
                bufptr += 48;
-               if (graft)
+               /*
+                * The clone is shallow if nr_parent < 0, and we must
+                * not traverse its real parents even when we unhide them.
+                */
+               if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
                        continue;
                new_parent = lookup_commit(parent);
                if (new_parent)
@@ -564,13 +564,13 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
        while (interesting(list)) {
                struct commit *commit;
                struct commit_list *parents;
-               struct commit_list *n;
+               struct commit_list *next;
                int flags;
 
                commit = list->item;
-               n = list->next;
+               next = list->next;
                free(list);
-               list = n;
+               list = next;
 
                flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
                if (flags == (PARENT1 | PARENT2)) {
@@ -598,11 +598,11 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
        free_commit_list(list);
        list = result; result = NULL;
        while (list) {
-               struct commit_list *n = list->next;
+               struct commit_list *next = list->next;
                if (!(list->item->object.flags & STALE))
                        insert_by_date(list->item, &result);
                free(list);
-               list = n;
+               list = next;
        }
        return result;
 }
index ba9f63813eba004ae409eba8741266a074161239..f4fc5c5589a3ae6f2b99e533c3dd0f3a7a7f418a 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -64,6 +64,7 @@ enum cmit_fmt {
 };
 
 extern int non_ascii(int);
+extern int has_non_ascii(const char *text);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 extern char *reencode_commit_message(const struct commit *commit,
                                     const char **encoding_p);
@@ -122,6 +123,8 @@ struct commit_graft *read_graft_line(char *buf, int len);
 int register_commit_graft(struct commit_graft *, int);
 struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
 
+const unsigned char *lookup_replace_object(const unsigned char *sha1);
+
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
 extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
 extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
@@ -137,6 +140,8 @@ int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit **, int);
 
 extern int interactive_add(int argc, const char **argv, const char *prefix);
+extern int run_add_interactive(const char *revision, const char *patch_mode,
+                              const char **pathspec);
 
 static inline int single_parent(struct commit *commit)
 {
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;
+}
diff --git a/compat/bswap.h b/compat/bswap.h
new file mode 100644 (file)
index 0000000..5cc4acb
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Let's make sure we always have a sane definition for ntohl()/htonl().
+ * Some libraries define those as a function call, just to perform byte
+ * shifting, bringing significant overhead to what should be a simple
+ * operation.
+ */
+
+/*
+ * Default version that the compiler ought to optimize properly with
+ * constant values.
+ */
+static inline uint32_t default_swab32(uint32_t val)
+{
+       return (((val & 0xff000000) >> 24) |
+               ((val & 0x00ff0000) >>  8) |
+               ((val & 0x0000ff00) <<  8) |
+               ((val & 0x000000ff) << 24));
+}
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+#define bswap32(x) ({ \
+       uint32_t __res; \
+       if (__builtin_constant_p(x)) { \
+               __res = default_swab32(x); \
+       } else { \
+               __asm__("bswap %0" : "=r" (__res) : "0" (x)); \
+       } \
+       __res; })
+
+#undef ntohl
+#undef htonl
+#define ntohl(x) bswap32(x)
+#define htonl(x) bswap32(x)
+
+#endif
index cdeda1d9859fd545950e0c39ee7de3c9dc09bb07..6b5b5b2c7011a89c0415ebb89406c4a3ac53e9a9 100644 (file)
@@ -1,8 +1,9 @@
 #include "../git-compat-util.h"
 #include "win32.h"
+#include <conio.h>
 #include "../strbuf.h"
 
-unsigned int _CRT_fmode = _O_BINARY;
+#include <shellapi.h>
 
 static int err_win_to_posix(DWORD winerr)
 {
@@ -122,13 +123,17 @@ int mingw_open (const char *filename, int oflags, ...)
 {
        va_list args;
        unsigned mode;
+       int fd;
+
        va_start(args, oflags);
        mode = va_arg(args, int);
        va_end(args);
 
        if (!strcmp(filename, "/dev/null"))
                filename = "nul";
-       int fd = open(filename, oflags, mode);
+
+       fd = open(filename, oflags, mode);
+
        if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
                DWORD attrs = GetFileAttributes(filename);
                if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
@@ -525,8 +530,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';
@@ -579,10 +584,11 @@ static char **get_path_split(void)
 
 static void free_path_split(char **path)
 {
+       char **p = path;
+
        if (!path)
                return;
 
-       char **p = path;
        while (*p)
                free(*p++);
        free(path);
@@ -823,7 +829,7 @@ void mingw_execvp(const char *cmd, char *const *argv)
        free_path_split(path);
 }
 
-char **copy_environ()
+static char **copy_environ(void)
 {
        char **env;
        int i = 0;
@@ -860,7 +866,7 @@ static int lookup_env(char **env, const char *name, size_t nmln)
 /*
  * If name contains '=', then sets the variable, otherwise it unsets it
  */
-char **env_setenv(char **env, const char *name)
+static char **env_setenv(char **env, const char *name)
 {
        char *eq = strchrnul(name, '=');
        int i = lookup_env(env, name, eq-name);
@@ -885,6 +891,18 @@ char **env_setenv(char **env, const char *name)
        return env;
 }
 
+/*
+ * Copies global environ and adjusts variables as specified by vars.
+ */
+char **make_augmented_environ(const char *const *vars)
+{
+       char **env = copy_environ();
+
+       while (*vars)
+               env = env_setenv(env, *vars++);
+       return env;
+}
+
 /* this is the first function to call into WS_32; initialize it */
 #undef gethostbyname
 struct hostent *mingw_gethostbyname(const char *host)
@@ -1011,7 +1029,7 @@ static sig_handler_t timer_fn = SIG_DFL;
  * length to call the signal handler.
  */
 
-static __stdcall unsigned ticktack(void *dummy)
+static unsigned __stdcall ticktack(void *dummy)
 {
        while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {
                if (timer_fn == SIG_DFL)
@@ -1107,9 +1125,9 @@ int sigaction(int sig, struct sigaction *in, struct sigaction *out)
 #undef signal
 sig_handler_t mingw_signal(int sig, sig_handler_t handler)
 {
+       sig_handler_t old = timer_fn;
        if (sig != SIGALRM)
                return signal(sig, handler);
-       sig_handler_t old = timer_fn;
        timer_fn = handler;
        return old;
 }
@@ -1138,7 +1156,7 @@ void mingw_open_html(const char *unixpath)
 
 int link(const char *oldpath, const char *newpath)
 {
-       typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
+       typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
        static T create_hard_link = NULL;
        if (!create_hard_link) {
                create_hard_link = (T) GetProcAddress(
@@ -1156,3 +1174,78 @@ 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)
+       {
+               DWORD lasterr;
+               handle = FindFirstFileA(dir->dd_name, &buf);
+               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..5b5258bcebbbead7e4ed15a4dea7bf755739b98e 100644 (file)
@@ -17,9 +17,10 @@ typedef int pid_t;
 #define S_IROTH 0
 #define S_IXOTH 0
 
-#define WIFEXITED(x) ((unsigned)(x) < 259)     /* STILL_ACTIVE */
+#define WIFEXITED(x) 1
+#define WIFSIGNALED(x) 0
 #define WEXITSTATUS(x) ((x) & 0xff)
-#define WIFSIGNALED(x) ((unsigned)(x) > 259)
+#define WTERMSIG(x) SIGTERM
 
 #define SIGHUP 1
 #define SIGQUIT 3
@@ -38,6 +39,9 @@ struct passwd {
        char *pw_dir;
 };
 
+extern char *getpass(const char *prompt);
+
+#ifndef POLLIN
 struct pollfd {
        int fd;           /* file descriptor */
        short events;     /* requested events */
@@ -45,6 +49,7 @@ struct pollfd {
 };
 #define POLLIN 1
 #define POLLHUP 2
+#endif
 
 typedef void (__cdecl *sig_handler_t)(int);
 struct sigaction {
@@ -90,6 +95,8 @@ static inline int fcntl(int fd, int cmd, long arg)
        errno = EINVAL;
        return -1;
 }
+/* bash cannot reliably detect negative return codes as failure */
+#define exit(code) exit((code) & 0xff)
 
 /*
  * simple adaptors
@@ -109,7 +116,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);
@@ -217,19 +224,52 @@ void mingw_open_html(const char *path);
  * helpers
  */
 
-char **copy_environ(void);
+char **make_augmented_environ(const char *const *vars);
 void free_environ(char **env);
-char **env_setenv(char **env, const char *name);
 
 /*
  * A replacement of main() that ensures that argv[0] has a path
+ * and that default fmode and std(in|out|err) are in binary mode
  */
 
 #define main(c,v) dummy_decl_mingw_main(); \
 static int mingw_main(); \
 int main(int argc, const char **argv) \
 { \
+       _fmode = _O_BINARY; \
+       _setmode(_fileno(stdin), _O_BINARY); \
+       _setmode(_fileno(stdout), _O_BINARY); \
+       _setmode(_fileno(stderr), _O_BINARY); \
        argv[0] = xstrdup(_pgmptr); \
        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/msvc.c b/compat/msvc.c
new file mode 100644 (file)
index 0000000..ac04a4c
--- /dev/null
@@ -0,0 +1,35 @@
+#include "../git-compat-util.h"
+#include "win32.h"
+#include <conio.h>
+#include "../strbuf.h"
+
+DIR *opendir(const char *name)
+{
+       int len;
+       DIR *p;
+       p = (DIR*)malloc(sizeof(DIR));
+       memset(p, 0, sizeof(DIR));
+       strncpy(p->dd_name, name, PATH_MAX);
+       len = strlen(p->dd_name);
+       p->dd_name[len] = '/';
+       p->dd_name[len+1] = '*';
+
+       if (p == NULL)
+               return NULL;
+
+       p->dd_handle = _findfirst(p->dd_name, &p->dd_dta);
+
+       if (p->dd_handle == -1) {
+               free(p);
+               return NULL;
+       }
+       return p;
+}
+int closedir(DIR *dir)
+{
+       _findclose(dir->dd_handle);
+       free(dir);
+       return 0;
+}
+
+#include "mingw.c"
diff --git a/compat/msvc.h b/compat/msvc.h
new file mode 100644 (file)
index 0000000..9c753a5
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef __MSVC__HEAD
+#define __MSVC__HEAD
+
+#include <direct.h>
+#include <process.h>
+#include <malloc.h>
+
+/* porting function */
+#define inline __inline
+#define __inline__ __inline
+#define __attribute__(x)
+#define va_copy(dst, src)     ((dst) = (src))
+#define strncasecmp  _strnicmp
+#define ftruncate    _chsize
+
+static __inline int strcasecmp (const char *s1, const char *s2)
+{
+       int size1 = strlen(s1);
+       int sisz2 = strlen(s2);
+       return _strnicmp(s1, s2, sisz2 > size1 ? sisz2 : size1);
+}
+
+#undef ERROR
+#undef stat
+#undef _stati64
+#include "compat/mingw.h"
+#undef stat
+#define stat _stati64
+#define _stat64(x,y) mingw_lstat(x,y)
+
+/*
+   Even though _stati64 is normally just defined at _stat64
+   on Windows, we specify it here as a proper struct to avoid
+   compiler warnings about macro redefinition due to magic in
+   mingw.h. Struct taken from ReactOS (GNU GPL license).
+*/
+struct _stati64 {
+       _dev_t  st_dev;
+       _ino_t  st_ino;
+       unsigned short st_mode;
+       short   st_nlink;
+       short   st_uid;
+       short   st_gid;
+       _dev_t  st_rdev;
+       __int64 st_size;
+       time_t  st_atime;
+       time_t  st_mtime;
+       time_t  st_ctime;
+};
+#endif
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 5ea007567d15c229685f571628d4afcce6783e8d..67d5c370a04c0630089e8601eafde5c05957959c 100644 (file)
@@ -4852,11 +4852,8 @@ regexec (preg, string, nmatch, pmatch, eflags)
    from either regcomp or regexec.   We don't use PREG here.  */
 
 size_t
-regerror (errcode, preg, errbuf, errbuf_size)
-    int errcode;
-    const regex_t *preg;
-    char *errbuf;
-    size_t errbuf_size;
+regerror(int errcode, const regex_t *preg,
+        char *errbuf, size_t errbuf_size)
 {
   const char *msg;
   size_t msg_size;
index 357e733074ea7c85f880fa577ad65dfb3787fec7..e1e0e7543d9414726122c121b7909bf73809a81a 100644 (file)
@@ -2,12 +2,19 @@
 
 /*
  * The size parameter specifies the available space, i.e. includes
- * the trailing NUL byte; but Windows's vsnprintf expects the
- * number of characters to write without the trailing NUL.
+ * the trailing NUL byte; but Windows's vsnprintf uses the entire
+ * buffer and avoids the trailing NUL, should the buffer be exactly
+ * big enough for the result. Defining SNPRINTF_SIZE_CORR to 1 will
+ * therefore remove 1 byte from the reported buffer size, so we
+ * always have room for a trailing NUL byte.
  */
 #ifndef SNPRINTF_SIZE_CORR
+#if defined(WIN32) && (!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)
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
new file mode 100644 (file)
index 0000000..df8a657
--- /dev/null
@@ -0,0 +1,50 @@
+The Steps of Build Git with VS2008
+
+1. You need the build environment, which contains the Git dependencies
+   to be able to compile, link and run Git with MSVC.
+
+   You can either use the binary repository:
+
+       WWW: http://repo.or.cz/w/msvcgit.git
+       Git: git clone git://repo.or.cz/msvcgit.git
+       Zip: http://repo.or.cz/w/msvcgit.git?a=snapshot;h=master;sf=zip
+
+   and call the setup_32bit_env.cmd batch script before compiling Git,
+   (see repo/package README for details), or the source repository:
+
+       WWW: http://repo.or.cz/w/gitbuild.git
+       Git: git clone git://repo.or.cz/gitbuild.git
+       Zip: (None, as it's a project with submodules)
+
+   and build the support libs as instructed in that repo/package.
+
+2. Ensure you have the msysgit environment in your path, so you have
+   GNU Make, bash and perl available.
+
+       WWW: http://repo.or.cz/w/msysgit.git
+       Git: git clone git://repo.or.cz/msysgit.git
+       Zip: http://repo.or.cz/w/msysgit.git?a=snapshot;h=master;sf=zip
+
+   This environment is also needed when you use the resulting
+   executables, since Git might need to run scripts which are part of
+   the git operations.
+
+3. Inside Git's directory run the command:
+       make common-cmds.h
+   to generate the common-cmds.h file needed to compile git.
+
+4. Then either build Git with the GNU Make Makefile in the Git projects
+   root
+       make MSVC=1
+   or generate Visual Studio solution/projects (.sln/.vcproj) with the
+   command
+       perl contrib/buildsystems/generate -g Vcproj
+   and open and build the solution with the IDE
+       devenv git.sln /useenv
+   or build with the IDE build engine directly from the command line
+       devenv git.sln /useenv /build "Release|Win32"
+   The /useenv option is required, so Visual Studio picks up the
+   environment variables for the support libraries required to build
+   Git, which you set up in step 1.
+
+Done!
diff --git a/compat/vcbuild/include/alloca.h b/compat/vcbuild/include/alloca.h
new file mode 100644 (file)
index 0000000..c0d7985
--- /dev/null
@@ -0,0 +1 @@
+#include <malloc.h>
diff --git a/compat/vcbuild/include/arpa/inet.h b/compat/vcbuild/include/arpa/inet.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/dirent.h b/compat/vcbuild/include/dirent.h
new file mode 100644 (file)
index 0000000..440618d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * DIRENT.H (formerly DIRLIB.H)
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is a part of the mingw-runtime package.
+ *
+ * The mingw-runtime package and its code is distributed in the hope that it
+ * will be useful but WITHOUT ANY WARRANTY.  ALL WARRANTIES, EXPRESSED OR
+ * IMPLIED ARE HEREBY DISCLAIMED.  This includes but is not limited to
+ * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You are free to use this package and its code without limitation.
+ */
+#ifndef _DIRENT_H_
+#define _DIRENT_H_
+#include <io.h>
+
+#define PATH_MAX 512
+
+#define __MINGW_NOTHROW
+
+#ifndef RC_INVOKED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dirent
+{
+       long            d_ino;          /* Always zero. */
+       unsigned short  d_reclen;       /* Always zero. */
+       unsigned short  d_namlen;       /* Length of name in d_name. */
+       char            d_name[FILENAME_MAX]; /* File name. */
+};
+
+/*
+ * This is an internal data structure. Good programmers will not use it
+ * except as an argument to one of the functions below.
+ * dd_stat field is now int (was short in older versions).
+ */
+typedef struct
+{
+       /* disk transfer area for this dir */
+       struct _finddata_t      dd_dta;
+
+       /* dirent struct to return from dir (NOTE: this makes this thread
+        * safe as long as only one thread uses a particular DIR struct at
+        * a time) */
+       struct dirent           dd_dir;
+
+       /* _findnext handle */
+       long                    dd_handle;
+
+       /*
+        * Status of search:
+        *   0 = not started yet (next entry to read is first entry)
+        *  -1 = off the end
+        *   positive = 0 based index of next entry
+        */
+       int                     dd_stat;
+
+       /* given path for dir with search pattern (struct is extended) */
+       char                    dd_name[PATH_MAX+3];
+} DIR;
+
+DIR* __cdecl __MINGW_NOTHROW opendir (const char*);
+struct dirent* __cdecl __MINGW_NOTHROW readdir (DIR*);
+int __cdecl __MINGW_NOTHROW closedir (DIR*);
+void __cdecl __MINGW_NOTHROW rewinddir (DIR*);
+long __cdecl __MINGW_NOTHROW telldir (DIR*);
+void __cdecl __MINGW_NOTHROW seekdir (DIR*, long);
+
+
+/* wide char versions */
+
+struct _wdirent
+{
+       long            d_ino;          /* Always zero. */
+       unsigned short  d_reclen;       /* Always zero. */
+       unsigned short  d_namlen;       /* Length of name in d_name. */
+       wchar_t         d_name[FILENAME_MAX]; /* File name. */
+};
+
+/*
+ * This is an internal data structure. Good programmers will not use it
+ * except as an argument to one of the functions below.
+ */
+typedef struct
+{
+       /* disk transfer area for this dir */
+       //struct _wfinddata_t   dd_dta;
+
+       /* dirent struct to return from dir (NOTE: this makes this thread
+        * safe as long as only one thread uses a particular DIR struct at
+        * a time) */
+       struct _wdirent         dd_dir;
+
+       /* _findnext handle */
+       long                    dd_handle;
+
+       /*
+        * Status of search:
+        *   0 = not started yet (next entry to read is first entry)
+        *  -1 = off the end
+        *   positive = 0 based index of next entry
+        */
+       int                     dd_stat;
+
+       /* given path for dir with search pattern (struct is extended) */
+       wchar_t                 dd_name[1];
+} _WDIR;
+
+
+
+_WDIR* __cdecl __MINGW_NOTHROW _wopendir (const wchar_t*);
+struct _wdirent*  __cdecl __MINGW_NOTHROW _wreaddir (_WDIR*);
+int __cdecl __MINGW_NOTHROW _wclosedir (_WDIR*);
+void __cdecl __MINGW_NOTHROW _wrewinddir (_WDIR*);
+long __cdecl __MINGW_NOTHROW _wtelldir (_WDIR*);
+void __cdecl __MINGW_NOTHROW _wseekdir (_WDIR*, long);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* Not RC_INVOKED */
+
+#endif /* Not _DIRENT_H_ */
diff --git a/compat/vcbuild/include/grp.h b/compat/vcbuild/include/grp.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/inttypes.h b/compat/vcbuild/include/inttypes.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/netdb.h b/compat/vcbuild/include/netdb.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/netinet/in.h b/compat/vcbuild/include/netinet/in.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/netinet/tcp.h b/compat/vcbuild/include/netinet/tcp.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/pwd.h b/compat/vcbuild/include/pwd.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/ioctl.h b/compat/vcbuild/include/sys/ioctl.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/param.h b/compat/vcbuild/include/sys/param.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/poll.h b/compat/vcbuild/include/sys/poll.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/select.h b/compat/vcbuild/include/sys/select.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/socket.h b/compat/vcbuild/include/sys/socket.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/time.h b/compat/vcbuild/include/sys/time.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/utime.h b/compat/vcbuild/include/sys/utime.h
new file mode 100644 (file)
index 0000000..582589c
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef        _UTIME_H_
+#define        _UTIME_H_
+/*
+ * UTIME.H
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is a part of the mingw-runtime package.
+ *
+ * The mingw-runtime package and its code is distributed in the hope that it
+ * will be useful but WITHOUT ANY WARRANTY.  ALL WARRANTIES, EXPRESSED OR
+ * IMPLIED ARE HEREBY DISCLAIMED.  This includes but is not limited to
+ * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You are free to use this package and its code without limitation.
+ */
+
+/*
+ * Structure used by _utime function.
+ */
+struct _utimbuf
+{
+       time_t  actime;         /* Access time */
+       time_t  modtime;        /* Modification time */
+};
+
+#ifndef        _NO_OLDNAMES
+/* NOTE: Must be the same as _utimbuf above. */
+struct utimbuf
+{
+       time_t  actime;
+       time_t  modtime;
+};
+#endif /* Not _NO_OLDNAMES */
+
+#endif
diff --git a/compat/vcbuild/include/sys/wait.h b/compat/vcbuild/include/sys/wait.h
new file mode 100644 (file)
index 0000000..0d8552a
--- /dev/null
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/unistd.h b/compat/vcbuild/include/unistd.h
new file mode 100644 (file)
index 0000000..2a4f276
--- /dev/null
@@ -0,0 +1,92 @@
+#ifndef _UNISTD_
+#define _UNISTD_
+
+/* Win32 define for porting git*/
+
+#ifndef _MODE_T_
+#define        _MODE_T_
+typedef unsigned short _mode_t;
+
+#ifndef        _NO_OLDNAMES
+typedef _mode_t        mode_t;
+#endif
+#endif /* Not _MODE_T_ */
+
+#ifndef _SSIZE_T_
+#define _SSIZE_T_
+typedef long _ssize_t;
+
+#ifndef        _OFF_T_
+#define        _OFF_T_
+typedef long _off_t;
+
+#ifndef        _NO_OLDNAMES
+typedef _off_t off_t;
+#endif
+#endif /* Not _OFF_T_ */
+
+
+#ifndef        _NO_OLDNAMES
+typedef _ssize_t ssize_t;
+#endif
+#endif /* Not _SSIZE_T_ */
+
+typedef signed char int8_t;
+typedef unsigned char   uint8_t;
+typedef short  int16_t;
+typedef unsigned short  uint16_t;
+typedef int  int32_t;
+typedef unsigned   uint32_t;
+typedef long long  int64_t;
+typedef unsigned long long   uint64_t;
+
+typedef long long  intmax_t;
+typedef unsigned long long uintmax_t;
+
+typedef int64_t off64_t;
+
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+/* Some defines for _access nAccessMode (MS doesn't define them, but
+ * it doesn't seem to hurt to add them). */
+#define        F_OK    0       /* Check for file existence */
+/* Well maybe it does hurt.  On newer versions of MSVCRT, an access mode
+   of 1 causes invalid parameter error. */
+#define        X_OK    0       /* MS access() doesn't check for execute permission. */
+#define        W_OK    2       /* Check for write permission */
+#define        R_OK    4       /* Check for read permission */
+
+#define        _S_IFIFO        0x1000  /* FIFO */
+#define        _S_IFCHR        0x2000  /* Character */
+#define        _S_IFBLK        0x3000  /* Block: Is this ever set under w32? */
+#define        _S_IFDIR        0x4000  /* Directory */
+#define        _S_IFREG        0x8000  /* Regular */
+
+#define        _S_IFMT         0xF000  /* File type mask */
+
+#define        _S_IXUSR        _S_IEXEC
+#define        _S_IWUSR        _S_IWRITE
+#define        _S_IRUSR        _S_IREAD
+#define        _S_ISDIR(m)     (((m) & _S_IFMT) == _S_IFDIR)
+
+#define        S_IFIFO         _S_IFIFO
+#define        S_IFCHR         _S_IFCHR
+#define        S_IFBLK         _S_IFBLK
+#define        S_IFDIR         _S_IFDIR
+#define        S_IFREG         _S_IFREG
+#define        S_IFMT          _S_IFMT
+#define        S_IEXEC         _S_IEXEC
+#define        S_IWRITE        _S_IWRITE
+#define        S_IREAD         _S_IREAD
+#define        S_IRWXU         _S_IRWXU
+#define        S_IXUSR         _S_IXUSR
+#define        S_IWUSR         _S_IWUSR
+#define        S_IRUSR         _S_IRUSR
+
+
+#define        S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
+#define        S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
+#define        S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)
+
+#endif
diff --git a/compat/vcbuild/include/utime.h b/compat/vcbuild/include/utime.h
new file mode 100644 (file)
index 0000000..8285f38
--- /dev/null
@@ -0,0 +1 @@
+#include <sys/utime.h>
diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl
new file mode 100644 (file)
index 0000000..f9528c0
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/perl -w
+######################################################################
+# Compiles or links files
+#
+# This is a wrapper to facilitate the compilation of Git with MSVC
+# using GNU Make as the build system. So, instead of manipulating the
+# Makefile into something nasty, just to support non-space arguments
+# etc, we use this wrapper to fix the command line options
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+my @args = ();
+my @cflags = ();
+my $is_linking = 0;
+while (@ARGV) {
+       my $arg = shift @ARGV;
+       if ("$arg" =~ /^-[DIMGO]/) {
+               push(@cflags, $arg);
+       } elsif ("$arg" eq "-o") {
+               my $file_out = shift @ARGV;
+               if ("$file_out" =~ /exe$/) {
+                       $is_linking = 1;
+                       push(@args, "-OUT:$file_out");
+               } else {
+                       push(@args, "-Fo$file_out");
+               }
+       } elsif ("$arg" eq "-lz") {
+               push(@args, "zlib.lib");
+       } elsif ("$arg" eq "-liconv") {
+               push(@args, "iconv.lib");
+       } elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") {
+               $arg =~ s/^-L/-LIBPATH:/;
+               push(@args, $arg);
+       } elsif ("$arg" =~ /^-R/) {
+               # eat
+       } else {
+               push(@args, $arg);
+       }
+}
+if ($is_linking) {
+       unshift(@args, "link.exe");
+} else {
+       unshift(@args, "cl.exe");
+       push(@args, @cflags);
+}
+#printf("**** @args\n");
+exit (system(@args) != 0);
diff --git a/compat/vcbuild/scripts/lib.pl b/compat/vcbuild/scripts/lib.pl
new file mode 100644 (file)
index 0000000..d8054e4
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+######################################################################
+# Libifies files on Windows
+#
+# This is a wrapper to facilitate the compilation of Git with MSVC
+# using GNU Make as the build system. So, instead of manipulating the
+# Makefile into something nasty, just to support non-space arguments
+# etc, we use this wrapper to fix the command line options
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+my @args = ();
+while (@ARGV) {
+       my $arg = shift @ARGV;
+       if ("$arg" eq "rcs") {
+               # Consume the rcs option
+       } elsif ("$arg" =~ /\.a$/) {
+               push(@args, "-OUT:$arg");
+       } else {
+               push(@args, $arg);
+       }
+}
+unshift(@args, "lib.exe");
+# printf("**** @args\n");
+exit (system(@args) != 0);
index c26384e595b4f23d5fef938b1136cc3a85469e56..8ce91048deffc17bdf8d51672ddaa0c867052ac5 100644 (file)
@@ -1,5 +1,10 @@
+#ifndef WIN32_H
+#define WIN32_H
+
 /* common Win32 functions for MinGW and Cygwin */
+#ifndef WIN32         /* Not defined by Cygwin */
 #include <windows.h>
+#endif
 
 static inline int file_attr_to_st_mode (DWORD attr)
 {
@@ -32,3 +37,5 @@ static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fd
                return ENOENT;
        }
 }
+
+#endif
index 44dc293ad314379d7835d9d96a5c3fd12ad2b27f..dedce2104eaf5cefbb1abef1b7921eb99c67a75e 100644 (file)
@@ -2,7 +2,6 @@
  * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
  */
 
-#include <windows.h>
 #include "../git-compat-util.h"
 
 /*
@@ -80,6 +79,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 +87,7 @@ static void erase_in_line(void)
        GetConsoleScreenBufferInfo(console, &sbi);
        FillConsoleOutputCharacterA(console, ' ',
                sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
-               NULL);
+               &dummy);
 }
 
 
index 1682273c12ab042d73fa32caf30d18fb13ef85e3..c64406113686e8886c0719206ce57d73fee13b29 100644 (file)
--- a/config.c
+++ b/config.c
@@ -62,7 +62,8 @@ static char *parse_value(void)
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
-                       space = 1;
+                       if (len)
+                               space++;
                        continue;
                }
                if (!quote) {
@@ -71,11 +72,8 @@ static char *parse_value(void)
                                continue;
                        }
                }
-               if (space) {
-                       if (len)
-                               value[len++] = ' ';
-                       space = 0;
-               }
+               for (; space; space--)
+                       value[len++] = ' ';
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
@@ -629,6 +627,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
        if (!prefixcmp(var, "mailmap."))
                return git_default_mailmap_config(var, value);
 
+       if (!prefixcmp(var, "advice."))
+               return git_default_advice_config(var, value);
+
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
                pager_use_color = git_config_bool(var,value);
                return 0;
@@ -1118,7 +1119,7 @@ int git_config_set_multivar(const char *key, const char *value,
                                    copy_end - copy_begin)
                                        goto write_err_out;
                                if (new_line &&
-                                   write_in_full(fd, "\n", 1) != 1)
+                                   write_str_in_full(fd, "\n") != 1)
                                        goto write_err_out;
                        }
                        copy_begin = store.offset[i];
@@ -1174,7 +1175,9 @@ write_err_out:
 static int section_name_match (const char *buf, const char *name)
 {
        int i = 0, j = 0, dot = 0;
-       for (; buf[i] && buf[i] != ']'; i++) {
+       if (buf[i] != '[')
+               return 0;
+       for (i = 1; buf[i] && buf[i] != ']'; i++) {
                if (!dot && isspace(buf[i])) {
                        dot = 1;
                        if (name[j++] != '.')
@@ -1195,7 +1198,17 @@ static int section_name_match (const char *buf, const char *name)
                if (buf[i] != name[j++])
                        break;
        }
-       return (buf[i] == ']' && name[j] == 0);
+       if (buf[i] == ']' && name[j] == 0) {
+               /*
+                * We match, now just find the right length offset by
+                * gobbling up any whitespace after it, as well
+                */
+               i++;
+               for (; buf[i] && isspace(buf[i]); i++)
+                       ; /* do nothing */
+               return i;
+       }
+       return 0;
 }
 
 /* if new_name == NULL, the section is removed instead */
@@ -1225,11 +1238,13 @@ int git_config_rename_section(const char *old_name, const char *new_name)
        while (fgets(buf, sizeof(buf), config_file)) {
                int i;
                int length;
+               char *output = buf;
                for (i = 0; buf[i] && isspace(buf[i]); i++)
                        ; /* do nothing */
                if (buf[i] == '[') {
                        /* it's a section */
-                       if (section_name_match (&buf[i+1], old_name)) {
+                       int offset = section_name_match(&buf[i], old_name);
+                       if (offset > 0) {
                                ret++;
                                if (new_name == NULL) {
                                        remove = 1;
@@ -1240,14 +1255,29 @@ int git_config_rename_section(const char *old_name, const char *new_name)
                                        ret = write_error(lock->filename);
                                        goto out;
                                }
-                               continue;
+                               /*
+                                * We wrote out the new section, with
+                                * a newline, now skip the old
+                                * section's length
+                                */
+                               output += offset + i;
+                               if (strlen(output) > 0) {
+                                       /*
+                                        * More content means there's
+                                        * a declaration to put on the
+                                        * next line; indent with a
+                                        * tab
+                                        */
+                                       output -= 1;
+                                       output[0] = '\t';
+                               }
                        }
                        remove = 0;
                }
                if (remove)
                        continue;
-               length = strlen(buf);
-               if (write_in_full(out_fd, buf, length) != length) {
+               length = strlen(output);
+               if (write_in_full(out_fd, output, length) != length) {
                        ret = write_error(lock->filename);
                        goto out;
                }
index 7cce0c12d507b222d47d8469abf59bf3ef9096b9..67b12f73a1bb795207082973701ece8009167f1c 100644 (file)
@@ -30,8 +30,11 @@ 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@
+NEEDS_LIBGEN=@NEEDS_LIBGEN@
 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 +49,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 4e728bca35f5f7b01188ef84df85b161266ce305..b09b8e446fbd52c55e6966c611b55ac1baa81f32 100644 (file)
@@ -156,19 +156,11 @@ AC_MSG_NOTICE([CHECKS for site configuration])
 # tests.  These tests take up a significant amount of the total test time
 # but are not needed unless you plan to talk to SVN repos.
 #
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
-#
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
+# This also implies BLK_SHA1.
 #
 # Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
 # /foo/bar/include and /foo/bar/lib directories.
@@ -342,9 +334,8 @@ GIT_STASH_FLAGS($OPENSSLDIR)
 AC_CHECK_LIB([crypto], [SHA1_Init],
 [NEEDS_SSL_WITH_CRYPTO=],
 [AC_CHECK_LIB([ssl], [SHA1_Init],
- [NEEDS_SSL_WITH_CRYPTO=YesPlease
-  NEEDS_SSL_WITH_CRYPTO=],
- [NO_OPENSSL=YesPlease])])
+ [NEEDS_SSL_WITH_CRYPTO=YesPlease],
+ [NEEDS_SSL_WITH_CRYPTO= NO_OPENSSL=YesPlease])])
 
 GIT_UNSTASH_FLAGS($OPENSSLDIR)
 
@@ -385,6 +376,8 @@ AC_SUBST(NO_EXPAT)
 # some Solaris installations).
 # Define NO_ICONV if neither libc nor libiconv support iconv.
 
+if test -z "$NO_ICONV"; then
+
 GIT_STASH_FLAGS($ICONVDIR)
 
 AC_DEFUN([ICONVTEST_SRC], [
@@ -431,6 +424,12 @@ GIT_UNSTASH_FLAGS($ICONVDIR)
 AC_SUBST(NEEDS_LIBICONV)
 AC_SUBST(NO_ICONV)
 
+if test -n "$NO_ICONV"; then
+    NEEDS_LIBICONV=
+fi
+
+fi
+
 #
 # Define NO_DEFLATE_BOUND if deflateBound is missing from zlib.
 
@@ -467,6 +466,21 @@ 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([c], [hstrerror],
+[NEEDS_RESOLV=],
+[NEEDS_RESOLV=YesPlease])
+AC_SUBST(NEEDS_RESOLV)
+test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv"
+
+AC_CHECK_LIB([c], [basename],
+[NEEDS_LIBGEN=],
+[NEEDS_LIBGEN=YesPlease])
+AC_SUBST(NEEDS_LIBGEN)
+test -n "$NEEDS_LIBGEN" && LIBS="$LIBS -lgen"
 
 ## Checks for header files.
 AC_MSG_NOTICE([CHECKS for header files])
@@ -627,6 +641,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=],
@@ -677,6 +697,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 f6b8ba6fec16ed2ff2de4c66e2465e79d059a817..7945e38ac1c99c09f7975ae99767bc9747ac3740 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -464,7 +464,7 @@ static void git_proxy_connect(int fd[2], char *host)
 
 #define MAX_CMD_LEN 1024
 
-char *get_port(char *host)
+static char *get_port(char *host)
 {
        char *end;
        char *p = strchr(host, ':');
@@ -513,7 +513,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
        signal(SIGCHLD, SIG_DFL);
 
        host = strstr(url, "://");
-       if(host) {
+       if (host) {
                *host = '\0';
                protocol = get_protocol(url);
                host += 3;
@@ -579,7 +579,10 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                        git_tcp_connect(fd, host, flags);
                /*
                 * Separate original protocol components prog and path
-                * from extended components with a NUL byte.
+                * from extended host header with a NUL byte.
+                *
+                * Note: Do not add any other headers here!  Doing so
+                * will cause older git-daemon servers to crash.
                 */
                packet_write(fd[1],
                             "%s %s%chost=%s%c",
@@ -602,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;
diff --git a/contrib/buildsystems/Generators.pm b/contrib/buildsystems/Generators.pm
new file mode 100644 (file)
index 0000000..408ef71
--- /dev/null
@@ -0,0 +1,42 @@
+package Generators;
+require Exporter;
+
+use strict;
+use File::Basename;
+no strict 'refs';
+use vars qw($VERSION @AVAILABLE);
+
+our $VERSION = '1.00';
+our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE);
+@ISA = qw(Exporter);
+
+BEGIN {
+    local(*D);
+    my $me = $INC{"Generators.pm"};
+    die "Couldn't find myself in \@INC, which is required to load the generators!" if ("$me" eq "");
+    $me = dirname($me);
+    if (opendir(D,"$me/Generators")) {
+        foreach my $gen (readdir(D)) {
+            next if ($gen  =~ /^\.\.?$/);
+            require "${me}/Generators/$gen";
+            $gen =~ s,\.pm,,;
+            push(@AVAILABLE, $gen);
+        }
+        closedir(D);
+        my $gens = join(', ', @AVAILABLE);
+    }
+
+    push @EXPORT_OK, qw(available);
+}
+
+sub available {
+    return @AVAILABLE;
+}
+
+sub generate {
+    my ($gen, $git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    return eval("Generators::${gen}::generate(\$git_dir, \$out_dir, \$rel_dir, \%build_structure)") if grep(/^$gen$/, @AVAILABLE);
+    die "Generator \"${gen}\" is not available!\nAvailable generators are: @AVAILABLE\n";
+}
+
+1;
diff --git a/contrib/buildsystems/Generators/QMake.pm b/contrib/buildsystems/Generators/QMake.pm
new file mode 100644 (file)
index 0000000..ff3b657
--- /dev/null
@@ -0,0 +1,189 @@
+package Generators::QMake;
+require Exporter;
+
+use strict;
+use vars qw($VERSION);
+
+our $VERSION = '1.00';
+our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE);
+@ISA = qw(Exporter);
+
+BEGIN {
+    push @EXPORT_OK, qw(generate);
+}
+
+sub generate {
+    my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+
+    my @libs = @{$build_structure{"LIBS"}};
+    foreach (@libs) {
+        createLibProject($_, $git_dir, $out_dir, $rel_dir, %build_structure);
+    }
+
+    my @apps = @{$build_structure{"APPS"}};
+    foreach (@apps) {
+        createAppProject($_, $git_dir, $out_dir, $rel_dir, %build_structure);
+    }
+
+    createGlueProject($git_dir, $out_dir, $rel_dir, %build_structure);
+    return 0;
+}
+
+sub createLibProject {
+    my ($libname, $git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    print "Generate $libname lib project\n";
+    $rel_dir = "../$rel_dir";
+
+    my $sources = join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"LIBS_${libname}_SOURCES"}})));
+    my $defines = join(" \\\n\t", sort(@{$build_structure{"LIBS_${libname}_DEFINES"}}));
+    my $includes= join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"LIBS_${libname}_INCLUDES"}})));
+    my $cflags  = join(" ", sort(@{$build_structure{"LIBS_${libname}_CFLAGS"}}));
+
+    my $cflags_debug = $cflags;
+    $cflags_debug =~ s/-MT/-MTd/;
+    $cflags_debug =~ s/-O.//;
+
+    my $cflags_release = $cflags;
+    $cflags_release =~ s/-MTd/-MT/;
+
+    my @tmp  = @{$build_structure{"LIBS_${libname}_LFLAGS"}};
+    my @tmp2 = ();
+    foreach (@tmp) {
+        if (/^-LTCG/) {
+        } elsif (/^-L/) {
+            $_ =~ s/^-L/-LIBPATH:$rel_dir\//;
+        }
+        push(@tmp2, $_);
+    }
+    my $lflags = join(" ", sort(@tmp));
+
+    my $target = $libname;
+    $target =~ s/\//_/g;
+    $defines =~ s/-D//g;
+    $defines =~ s/"/\\\\"/g;
+    $includes =~ s/-I//g;
+    mkdir "$target" || die "Could not create the directory $target for lib project!\n";
+    open F, ">$target/$target.pro" || die "Could not open $target/$target.pro for writing!\n";
+    print F << "EOM";
+TEMPLATE = lib
+TARGET = $target
+DESTDIR = $rel_dir
+
+CONFIG -= qt
+CONFIG += static
+
+QMAKE_CFLAGS =
+QMAKE_CFLAGS_RELEASE = $cflags_release
+QMAKE_CFLAGS_DEBUG = $cflags_debug
+QMAKE_LIBFLAGS = $lflags
+
+DEFINES += \\
+        $defines
+
+INCLUDEPATH += \\
+        $includes
+
+SOURCES += \\
+        $sources
+EOM
+    close F;
+}
+
+sub createAppProject {
+    my ($appname, $git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    print "Generate $appname app project\n";
+    $rel_dir = "../$rel_dir";
+
+    my $sources = join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"APPS_${appname}_SOURCES"}})));
+    my $defines = join(" \\\n\t", sort(@{$build_structure{"APPS_${appname}_DEFINES"}}));
+    my $includes= join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"APPS_${appname}_INCLUDES"}})));
+    my $cflags  = join(" ", sort(@{$build_structure{"APPS_${appname}_CFLAGS"}}));
+
+    my $cflags_debug = $cflags;
+    $cflags_debug =~ s/-MT/-MTd/;
+    $cflags_debug =~ s/-O.//;
+
+    my $cflags_release = $cflags;
+    $cflags_release =~ s/-MTd/-MT/;
+
+    my $libs;
+    foreach (sort(@{$build_structure{"APPS_${appname}_LIBS"}})) {
+        $_ =~ s/\//_/g;
+        $libs .= " $_";
+    }
+    my @tmp  = @{$build_structure{"APPS_${appname}_LFLAGS"}};
+    my @tmp2 = ();
+    foreach (@tmp) {
+        # next if ($_ eq "-NODEFAULTLIB:MSVCRT.lib");
+        if (/^-LTCG/) {
+        } elsif (/^-L/) {
+            $_ =~ s/^-L/-LIBPATH:$rel_dir\//;
+        }
+        push(@tmp2, $_);
+    }
+    my $lflags = join(" ", sort(@tmp));
+
+    my $target = $appname;
+    $target =~ s/\.exe//;
+    $target =~ s/\//_/g;
+    $defines =~ s/-D//g;
+    $defines =~ s/"/\\\\"/g;
+    $includes =~ s/-I//g;
+    mkdir "$target" || die "Could not create the directory $target for app project!\n";
+    open F, ">$target/$target.pro" || die "Could not open $target/$target.pro for writing!\n";
+    print F << "EOM";
+TEMPLATE = app
+TARGET = $target
+DESTDIR = $rel_dir
+
+CONFIG -= qt embed_manifest_exe
+CONFIG += console
+
+QMAKE_CFLAGS =
+QMAKE_CFLAGS_RELEASE = $cflags_release
+QMAKE_CFLAGS_DEBUG = $cflags_debug
+QMAKE_LFLAGS = $lflags
+LIBS   = $libs
+
+DEFINES += \\
+        $defines
+
+INCLUDEPATH += \\
+        $includes
+
+win32:QMAKE_LFLAGS += -LIBPATH:$rel_dir
+else: QMAKE_LFLAGS += -L$rel_dir
+
+SOURCES += \\
+        $sources
+EOM
+    close F;
+}
+
+sub createGlueProject {
+    my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    my $libs = join(" \\ \n", map("\t$_|$_.pro", @{$build_structure{"LIBS"}}));
+    my $apps = join(" \\ \n", map("\t$_|$_.pro", @{$build_structure{"APPS"}}));
+    $libs =~ s/\.a//g;
+    $libs =~ s/\//_/g;
+    $libs =~ s/\|/\//g;
+    $apps =~ s/\.exe//g;
+    $apps =~ s/\//_/g;
+    $apps =~ s/\|/\//g;
+
+    my $filename = $out_dir;
+    $filename =~ s/.*\/([^\/]+)$/$1/;
+    $filename =~ s/\/$//;
+    print "Generate glue project $filename.pro\n";
+    open F, ">$filename.pro" || die "Could not open $filename.pro for writing!\n";
+    print F << "EOM";
+TEMPLATE = subdirs
+CONFIG += ordered
+SUBDIRS += \\
+$libs \\
+$apps
+EOM
+    close F;
+}
+
+1;
diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm
new file mode 100644 (file)
index 0000000..be94ba1
--- /dev/null
@@ -0,0 +1,622 @@
+package Generators::Vcproj;
+require Exporter;
+
+use strict;
+use vars qw($VERSION);
+
+our $VERSION = '1.00';
+our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE);
+@ISA = qw(Exporter);
+
+BEGIN {
+    push @EXPORT_OK, qw(generate);
+}
+
+my $guid_index = 0;
+my @GUIDS = (
+    "{E07B9989-2BF7-4F21-8918-BE22BA467AC3}",
+    "{278FFB51-0296-4A44-A81A-22B87B7C3592}",
+    "{7346A2C4-F0FD-444F-9EBE-1AF23B2B5650}",
+    "{67F421AC-EB34-4D49-820B-3196807B423F}",
+    "{385DCFE1-CC8C-4211-A451-80FCFC31CA51}",
+    "{97CC46C5-D2CC-4D26-B634-E75792B79916}",
+    "{C7CE21FE-6EF8-4012-A5C7-A22BCEDFBA11}",
+    "{51575134-3FDF-42D1-BABD-3FB12669C6C9}",
+    "{0AE195E4-9823-4B87-8E6F-20C5614AF2FF}",
+    "{4B918255-67CA-43BB-A46C-26704B666E6B}",
+    "{18CCFEEF-C8EE-4CC1-A265-26F95C9F4649}",
+    "{5D5D90FA-01B7-4973-AFE5-CA88C53AC197}",
+    "{1F054320-036D-49E1-B384-FB5DF0BC8AC0}",
+    "{7CED65EE-F2D9-4171-825B-C7D561FE5786}",
+    "{8D341679-0F07-4664-9A56-3BA0DE88B9BC}",
+    "{C189FEDC-2957-4BD7-9FA4-7622241EA145}",
+    "{66844203-1B9F-4C53-9274-164FFF95B847}",
+    "{E4FEA145-DECC-440D-AEEA-598CF381FD43}",
+    "{73300A8E-C8AC-41B0-B555-4F596B681BA7}",
+    "{873FDEB1-D01D-40BF-A1BF-8BBC58EC0F51}",
+    "{7922C8BE-76C5-4AC6-8BF7-885C0F93B782}",
+    "{E245D370-308B-4A49-BFC1-1E527827975F}",
+    "{F6FA957B-66FC-4ED7-B260-E59BBE4FE813}",
+    "{E6055070-0198-431A-BC49-8DB6CEE770AE}",
+    "{54159234-C3EB-43DA-906B-CE5DA5C74654}",
+    "{594CFC35-0B60-46F6-B8EF-9983ACC1187D}",
+    "{D93FCAB7-1F01-48D2-B832-F761B83231A5}",
+    "{DBA5E6AC-E7BE-42D3-8703-4E787141526E}",
+    "{6171953F-DD26-44C7-A3BE-CC45F86FC11F}",
+    "{9E19DDBE-F5E4-4A26-A2FE-0616E04879B8}",
+    "{AE81A615-99E3-4885-9CE0-D9CAA193E867}",
+    "{FBF4067E-1855-4F6C-8BCD-4D62E801A04D}",
+    "{17007948-6593-4AEB-8106-F7884B4F2C19}",
+    "{199D4C8D-8639-4DA6-82EF-08668C35DEE0}",
+    "{E085E50E-C140-4CF3-BE4B-094B14F0DDD6}",
+    "{00785268-A9CC-4E40-AC29-BAC0019159CE}",
+    "{4C06F56A-DCDB-46A6-B67C-02339935CF12}",
+    "{3A62D3FD-519E-4EC9-8171-D2C1BFEA022F}",
+    "{3A62D3FD-519E-4EC9-8171-D2C1BFEA022F}",
+    "{9392EB58-D7BA-410B-B1F0-B2FAA6BC89A7}",
+    "{2ACAB2D5-E0CE-4027-BCA0-D78B2D7A6C66}",
+    "{86E216C3-43CE-481A-BCB2-BE5E62850635}",
+    "{FB631291-7923-4B91-9A57-7B18FDBB7A42}",
+    "{0A176EC9-E934-45B8-B87F-16C7F4C80039}",
+    "{DF55CA80-46E8-4C53-B65B-4990A23DD444}",
+    "{3A0F9895-55D2-4710-BE5E-AD7498B5BF44}",
+    "{294BDC5A-F448-48B6-8110-DD0A81820F8C}",
+    "{4B9F66E9-FAC9-47AB-B1EF-C16756FBFD06}",
+    "{72EA49C6-2806-48BD-B81B-D4905102E19C}",
+    "{5728EB7E-8929-486C-8CD5-3238D060E768}"
+);
+
+sub generate {
+    my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    my @libs = @{$build_structure{"LIBS"}};
+    foreach (@libs) {
+        createLibProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure);
+    }
+
+    my @apps = @{$build_structure{"APPS"}};
+    foreach (@apps) {
+        createAppProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure);
+    }
+
+    createGlueProject($git_dir, $out_dir, $rel_dir, %build_structure);
+    return 0;
+}
+
+sub createLibProject {
+    my ($libname, $git_dir, $out_dir, $rel_dir, $build_structure) = @_;
+    print "Generate $libname vcproj lib project\n";
+    $rel_dir = "..\\$rel_dir";
+    $rel_dir =~ s/\//\\/g;
+
+    my $target = $libname;
+    $target =~ s/\//_/g;
+    $target =~ s/\.a//;
+
+    my $uuid = $GUIDS[$guid_index];
+    $$build_structure{"LIBS_${target}_GUID"} = $uuid;
+    $guid_index += 1;
+
+    my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"LIBS_${libname}_SOURCES"}}));
+    my @sources;
+    foreach (@srcs) {
+        $_ =~ s/\//\\/g;
+        push(@sources, $_);
+    }
+    my $defines = join(",", sort(@{$$build_structure{"LIBS_${libname}_DEFINES"}}));
+    my $includes= join(";", sort(map("&quot;$rel_dir\\$_&quot;", @{$$build_structure{"LIBS_${libname}_INCLUDES"}})));
+    my $cflags  = join(" ", sort(@{$$build_structure{"LIBS_${libname}_CFLAGS"}}));
+    $cflags =~ s/\"/&quot;/g;
+
+    my $cflags_debug = $cflags;
+    $cflags_debug =~ s/-MT/-MTd/;
+    $cflags_debug =~ s/-O.//;
+
+    my $cflags_release = $cflags;
+    $cflags_release =~ s/-MTd/-MT/;
+
+    my @tmp  = @{$$build_structure{"LIBS_${libname}_LFLAGS"}};
+    my @tmp2 = ();
+    foreach (@tmp) {
+        if (/^-LTCG/) {
+        } elsif (/^-L/) {
+            $_ =~ s/^-L/-LIBPATH:$rel_dir\//;
+        }
+        push(@tmp2, $_);
+    }
+    my $lflags = join(" ", sort(@tmp));
+
+    $defines =~ s/-D//g;
+    $defines =~ s/\"/\\&quot;/g;
+    $defines =~ s/\'//g;
+    $includes =~ s/-I//g;
+    mkdir "$target" || die "Could not create the directory $target for lib project!\n";
+    open F, ">$target/$target.vcproj" || die "Could not open $target/$target.pro for writing!\n";
+    binmode F, ":crlf";
+    print F << "EOM";
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="9,00"
+       Name="$target"
+       ProjectGUID="$uuid">
+       <Platforms>
+               <Platform
+                       Name="Win32"/>
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory="$rel_dir"
+                       ConfigurationType="4"
+                       CharacterSet="0"
+                       IntermediateDirectory="\$(ProjectDir)\$(ConfigurationName)"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="$cflags_debug"
+                               Optimization="0"
+                               InlineFunctionExpansion="1"
+                               AdditionalIncludeDirectories="$includes"
+                               PreprocessorDefinitions="WIN32,_DEBUG,$defines"
+                               MinimalRebuild="true"
+                               RuntimeLibrary="1"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLibrarianTool"
+                               SuppressStartupBanner="true"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory="$rel_dir"
+                       ConfigurationType="4"
+                       CharacterSet="0"
+                       WholeProgramOptimization="1"
+                       IntermediateDirectory="\$(ProjectDir)\$(ConfigurationName)"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="$cflags_release"
+                               Optimization="2"
+                               InlineFunctionExpansion="1"
+                               EnableIntrinsicFunctions="true"
+                               AdditionalIncludeDirectories="$includes"
+                               PreprocessorDefinitions="WIN32,NDEBUG,$defines"
+                               RuntimeLibrary="0"
+                               EnableFunctionLevelLinking="true"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLibrarianTool"
+                               SuppressStartupBanner="true"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+       </Configurations>
+       <Files>
+               <Filter
+                       Name="Source Files"
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+EOM
+    foreach(@sources) {
+        print F << "EOM";
+                       <File
+                               RelativePath="$_"/>
+EOM
+    }
+    print F << "EOM";
+               </Filter>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
+EOM
+    close F;
+}
+
+sub createAppProject {
+    my ($appname, $git_dir, $out_dir, $rel_dir, $build_structure) = @_;
+    print "Generate $appname vcproj app project\n";
+    $rel_dir = "..\\$rel_dir";
+    $rel_dir =~ s/\//\\/g;
+
+    my $target = $appname;
+    $target =~ s/\//_/g;
+    $target =~ s/\.exe//;
+
+    my $uuid = $GUIDS[$guid_index];
+    $$build_structure{"APPS_${target}_GUID"} = $uuid;
+    $guid_index += 1;
+
+    my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"APPS_${appname}_SOURCES"}}));
+    my @sources;
+    foreach (@srcs) {
+        $_ =~ s/\//\\/g;
+        push(@sources, $_);
+    }
+    my $defines = join(",", sort(@{$$build_structure{"APPS_${appname}_DEFINES"}}));
+    my $includes= join(";", sort(map("&quot;$rel_dir\\$_&quot;", @{$$build_structure{"APPS_${appname}_INCLUDES"}})));
+    my $cflags  = join(" ", sort(@{$$build_structure{"APPS_${appname}_CFLAGS"}}));
+    $cflags =~ s/\"/&quot;/g;
+
+    my $cflags_debug = $cflags;
+    $cflags_debug =~ s/-MT/-MTd/;
+    $cflags_debug =~ s/-O.//;
+
+    my $cflags_release = $cflags;
+    $cflags_release =~ s/-MTd/-MT/;
+
+    my $libs;
+    foreach (sort(@{$$build_structure{"APPS_${appname}_LIBS"}})) {
+        $_ =~ s/\//_/g;
+        $libs .= " $_";
+    }
+    my @tmp  = @{$$build_structure{"APPS_${appname}_LFLAGS"}};
+    my @tmp2 = ();
+    foreach (@tmp) {
+        if (/^-LTCG/) {
+        } elsif (/^-L/) {
+            $_ =~ s/^-L/-LIBPATH:$rel_dir\//;
+        }
+        push(@tmp2, $_);
+    }
+    my $lflags = join(" ", sort(@tmp)) . " -LIBPATH:$rel_dir";
+
+    $defines =~ s/-D//g;
+    $defines =~ s/\"/\\&quot;/g;
+    $defines =~ s/\'//g;
+    $defines =~ s/\\\\/\\/g;
+    $includes =~ s/-I//g;
+    mkdir "$target" || die "Could not create the directory $target for lib project!\n";
+    open F, ">$target/$target.vcproj" || die "Could not open $target/$target.pro for writing!\n";
+    binmode F, ":crlf";
+    print F << "EOM";
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="9,00"
+       Name="$target"
+       ProjectGUID="$uuid">
+       <Platforms>
+               <Platform
+                       Name="Win32"/>
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory="$rel_dir"
+                       ConfigurationType="1"
+                       CharacterSet="0"
+                       IntermediateDirectory="\$(ProjectDir)\$(ConfigurationName)"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="$cflags_debug"
+                               Optimization="0"
+                               InlineFunctionExpansion="1"
+                               AdditionalIncludeDirectories="$includes"
+                               PreprocessorDefinitions="WIN32,_DEBUG,$defines"
+                               MinimalRebuild="true"
+                               RuntimeLibrary="1"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="$libs"
+                               AdditionalOptions="$lflags"
+                               LinkIncremental="2"
+                               GenerateDebugInformation="true"
+                               SubSystem="1"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory="$rel_dir"
+                       ConfigurationType="1"
+                       CharacterSet="0"
+                       WholeProgramOptimization="1"
+                       IntermediateDirectory="\$(ProjectDir)\$(ConfigurationName)"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="$cflags_release"
+                               Optimization="2"
+                               InlineFunctionExpansion="1"
+                               EnableIntrinsicFunctions="true"
+                               AdditionalIncludeDirectories="$includes"
+                               PreprocessorDefinitions="WIN32,NDEBUG,$defines"
+                               RuntimeLibrary="0"
+                               EnableFunctionLevelLinking="true"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="$libs"
+                               AdditionalOptions="$lflags"
+                               LinkIncremental="1"
+                               GenerateDebugInformation="true"
+                               SubSystem="1"
+                               TargetMachine="1"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+       </Configurations>
+       <Files>
+               <Filter
+                       Name="Source Files"
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+EOM
+    foreach(@sources) {
+        print F << "EOM";
+                       <File
+                               RelativePath="$_"/>
+EOM
+    }
+    print F << "EOM";
+               </Filter>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
+EOM
+    close F;
+}
+
+sub createGlueProject {
+    my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    print "Generate solutions file\n";
+    $rel_dir = "..\\$rel_dir";
+    $rel_dir =~ s/\//\\/g;
+    my $SLN_HEAD = "Microsoft Visual Studio Solution File, Format Version 10.00\n# Visual Studio 2008\n";
+    my $SLN_PRE  = "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = ";
+    my $SLN_POST = "\nEndProject\n";
+
+    my @libs = @{$build_structure{"LIBS"}};
+    my @tmp;
+    foreach (@libs) {
+        $_ =~ s/\//_/g;
+        $_ =~ s/\.a//;
+        push(@tmp, $_);
+    }
+    @libs = @tmp;
+
+    my @apps = @{$build_structure{"APPS"}};
+    @tmp = ();
+    foreach (@apps) {
+        $_ =~ s/\//_/g;
+        $_ =~ s/\.exe//;
+        push(@tmp, $_);
+    }
+    @apps = @tmp;
+
+    open F, ">git.sln" || die "Could not open git.sln for writing!\n";
+    binmode F, ":crlf";
+    print F "$SLN_HEAD";
+    foreach (@libs) {
+        my $libname = $_;
+        my $uuid = $build_structure{"LIBS_${libname}_GUID"};
+        print F "$SLN_PRE";
+        print F "\"${libname}\", \"${libname}\\${libname}.vcproj\", \"${uuid}\"";
+        print F "$SLN_POST";
+    }
+    my $uuid_libgit = $build_structure{"LIBS_libgit_GUID"};
+    my $uuid_xdiff_lib = $build_structure{"LIBS_xdiff_lib_GUID"};
+    foreach (@apps) {
+        my $appname = $_;
+        my $uuid = $build_structure{"APPS_${appname}_GUID"};
+        print F "$SLN_PRE";
+        print F "\"${appname}\", \"${appname}\\${appname}.vcproj\", \"${uuid}\"\n";
+        print F "      ProjectSection(ProjectDependencies) = postProject\n";
+        print F "              ${uuid_libgit} = ${uuid_libgit}\n";
+        print F "              ${uuid_xdiff_lib} = ${uuid_xdiff_lib}\n";
+        print F "      EndProjectSection";
+        print F "$SLN_POST";
+    }
+
+    print F << "EOM";
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Win32 = Debug|Win32
+               Release|Win32 = Release|Win32
+       EndGlobalSection
+EOM
+    print F << "EOM";
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+EOM
+    foreach (@libs) {
+        my $libname = $_;
+        my $uuid = $build_structure{"LIBS_${libname}_GUID"};
+        print F "\t\t${uuid}.Debug|Win32.ActiveCfg = Debug|Win32\n";
+        print F "\t\t${uuid}.Debug|Win32.Build.0 = Debug|Win32\n";
+        print F "\t\t${uuid}.Release|Win32.ActiveCfg = Release|Win32\n";
+        print F "\t\t${uuid}.Release|Win32.Build.0 = Release|Win32\n";
+    }
+    foreach (@apps) {
+        my $appname = $_;
+        my $uuid = $build_structure{"APPS_${appname}_GUID"};
+        print F "\t\t${uuid}.Debug|Win32.ActiveCfg = Debug|Win32\n";
+        print F "\t\t${uuid}.Debug|Win32.Build.0 = Debug|Win32\n";
+        print F "\t\t${uuid}.Release|Win32.ActiveCfg = Release|Win32\n";
+        print F "\t\t${uuid}.Release|Win32.Build.0 = Release|Win32\n";
+    }
+
+    print F << "EOM";
+       EndGlobalSection
+EndGlobal
+EOM
+    close F;
+}
+
+1;
diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl
new file mode 100644 (file)
index 0000000..20bd061
--- /dev/null
@@ -0,0 +1,353 @@
+#!/usr/bin/perl -w
+######################################################################
+# Do not call this script directly!
+#
+# The generate script ensures that @INC is correct before the engine
+# is executed.
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+use File::Basename;
+use File::Spec;
+use Cwd;
+use Generators;
+
+my (%build_structure, %compile_options, @makedry);
+my $out_dir = getcwd();
+my $git_dir = $out_dir;
+$git_dir =~ s=\\=/=g;
+$git_dir = dirname($git_dir) while (!-e "$git_dir/git.c" && "$git_dir" ne "");
+die "Couldn't find Git repo" if ("$git_dir" eq "");
+
+my @gens = Generators::available();
+my $gen = "Vcproj";
+
+sub showUsage
+{
+    my $genlist = join(', ', @gens);
+    print << "EOM";
+generate usage:
+  -g <GENERATOR>  --gen <GENERATOR> Specify the buildsystem generator    (default: $gen)
+                                    Available: $genlist
+  -o <PATH>       --out <PATH>      Specify output directory generation  (default: .)
+  -i <FILE>       --in <FILE>       Specify input file, instead of running GNU Make
+  -h,-?           --help            This help
+EOM
+    exit 0;
+}
+
+# Parse command-line options
+while (@ARGV) {
+    my $arg = shift @ARGV;
+    if ("$arg" eq "-h" || "$arg" eq "--help" || "$arg" eq "-?") {
+       showUsage();
+       exit(0);
+    } elsif("$arg" eq "--out" || "$arg" eq "-o") {
+       $out_dir = shift @ARGV;
+    } elsif("$arg" eq "--gen" || "$arg" eq "-g") {
+       $gen = shift @ARGV;
+    } elsif("$arg" eq "--in" || "$arg" eq "-i") {
+       my $infile = shift @ARGV;
+        open(F, "<$infile") || die "Couldn't open file $infile";
+        @makedry = <F>;
+        close(F);
+    }
+}
+
+# NOT using File::Spec->rel2abs($path, $base) here, as
+# it fails badly for me in the msysgit environment
+$git_dir = File::Spec->rel2abs($git_dir);
+$out_dir = File::Spec->rel2abs($out_dir);
+my $rel_dir = makeOutRel2Git($git_dir, $out_dir);
+
+# Print some information so the user feels informed
+print << "EOM";
+-----
+Generator: $gen
+Git dir:   $git_dir
+Out dir:   $out_dir
+-----
+Running GNU Make to figure out build structure...
+EOM
+
+# Pipe a make --dry-run into a variable, if not already loaded from file
+@makedry = `cd $git_dir && make -n MSVC=1 V=1 2>/dev/null` if !@makedry;
+
+# Parse the make output into usable info
+parseMakeOutput();
+
+# Finally, ask the generator to start generating..
+Generators::generate($gen, $git_dir, $out_dir, $rel_dir, %build_structure);
+
+# main flow ends here
+# -------------------------------------------------------------------------------------------------
+
+
+# 1) path: /foo/bar/baz        2) path: /foo/bar/baz   3) path: /foo/bar/baz
+#    base: /foo/bar/baz/temp      base: /foo/bar          base: /tmp
+#    rel:  ..                     rel:  baz               rel:  ../foo/bar/baz
+sub makeOutRel2Git
+{
+    my ($path, $base) = @_;
+    my $rel;
+    if ("$path" eq "$base") {
+        return ".";
+    } elsif ($base =~ /^$path/) {
+        # case 1
+        my $tmp = $base;
+        $tmp =~ s/^$path//;
+        foreach (split('/', $tmp)) {
+            $rel .= "../" if ("$_" ne "");
+        }
+    } elsif ($path =~ /^$base/) {
+        # case 2
+        $rel = $path;
+        $rel =~ s/^$base//;
+        $rel = "./$rel";
+    } else {
+        my $tmp = $base;
+        foreach (split('/', $tmp)) {
+            $rel .= "../" if ("$_" ne "");
+        }
+        $rel .= $path;
+    }
+    $rel =~ s/\/\//\//g; # simplify
+    $rel =~ s/\/$//;     # don't end with /
+    return $rel;
+}
+
+sub parseMakeOutput
+{
+    print "Parsing GNU Make output to figure out build structure...\n";
+    my $line = 0;
+    while (my $text = shift @makedry) {
+        my $ate_next;
+        do {
+            $ate_next = 0;
+            $line++;
+            chomp $text;
+            chop $text if ($text =~ /\r$/);
+            if ($text =~ /\\$/) {
+                $text =~ s/\\$//;
+                $text .= shift @makedry;
+                $ate_next = 1;
+            }
+        } while($ate_next);
+
+        if($text =~ / -c /) {
+            # compilation
+            handleCompileLine($text, $line);
+
+        } elsif ($text =~ / -o /) {
+            # linking executable
+            handleLinkLine($text, $line);
+
+        } elsif ($text =~ /\.o / && $text =~ /\.a /) {
+            # libifying
+            handleLibLine($text, $line);
+#
+#        } elsif ($text =~ /^cp /) {
+#            # copy file around
+#
+#        } elsif ($text =~ /^rm -f /) {
+#            # shell command
+#
+#        } elsif ($text =~ /^make[ \[]/) {
+#            # make output
+#
+#        } elsif ($text =~ /^echo /) {
+#            # echo to file
+#
+#        } elsif ($text =~ /^if /) {
+#            # shell conditional
+#
+#        } elsif ($text =~ /^tclsh /) {
+#            # translation stuff
+#
+#        } elsif ($text =~ /^umask /) {
+#            # handling boilerplates
+#
+#        } elsif ($text =~ /\$\(\:\)/) {
+#            # ignore
+#
+#        } elsif ($text =~ /^FLAGS=/) {
+#            # flags check for dependencies
+#
+#        } elsif ($text =~ /^'\/usr\/bin\/perl' -MError -e/) {
+#            # perl commands for copying files
+#
+#        } elsif ($text =~ /generate-cmdlist\.sh/) {
+#            # command for generating list of commands
+#
+#        } elsif ($text =~ /^test / && $text =~ /|| rm -f /) {
+#            # commands removing executables, if they exist
+#
+#        } elsif ($text =~ /new locations or Tcl/) {
+#            # command for detecting Tcl/Tk changes
+#
+#        } elsif ($text =~ /mkdir -p/) {
+#            # command creating path
+#
+#        } elsif ($text =~ /: no custom templates yet/) {
+#            # whatever
+#
+#        } else {
+#            print "Unhandled (line: $line): $text\n";
+        }
+    }
+
+#    use Data::Dumper;
+#    print "Parsed build structure:\n";
+#    print Dumper(%build_structure);
+}
+
+# variables for the compilation part of each step
+my (@defines, @incpaths, @cflags, @sources);
+
+sub clearCompileStep
+{
+    @defines = ();
+    @incpaths = ();
+    @cflags = ();
+    @sources = ();
+}
+
+sub removeDuplicates
+{
+    my (%dupHash, $entry);
+    %dupHash = map { $_, 1 } @defines;
+    @defines = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @incpaths;
+    @incpaths = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @cflags;
+    @cflags = keys %dupHash;
+}
+
+sub handleCompileLine
+{
+    my ($line, $lineno) = @_;
+    my @parts = split(' ', $line);
+    my $sourcefile;
+    shift(@parts); # ignore cmd
+    while (my $part = shift @parts) {
+        if ("$part" eq "-o") {
+            # ignore object file
+            shift @parts;
+        } elsif ("$part" eq "-c") {
+            # ignore compile flag
+        } elsif ("$part" eq "-c") {
+        } elsif ($part =~ /^.?-I/) {
+            push(@incpaths, $part);
+        } elsif ($part =~ /^.?-D/) {
+            push(@defines, $part);
+        } elsif ($part =~ /^-/) {
+            push(@cflags, $part);
+        } elsif ($part =~ /\.(c|cc|cpp)$/) {
+            $sourcefile = $part;
+        } else {
+            die "Unhandled compiler option @ line $lineno: $part";
+        }
+    }
+    @{$compile_options{"${sourcefile}_CFLAGS"}} = @cflags;
+    @{$compile_options{"${sourcefile}_DEFINES"}} = @defines;
+    @{$compile_options{"${sourcefile}_INCPATHS"}} = @incpaths;
+    clearCompileStep();
+}
+
+sub handleLibLine
+{
+    my ($line, $lineno) = @_;
+    my (@objfiles, @lflags, $libout, $part);
+    # kill cmd and rm 'prefix'
+    $line =~ s/^rm -f .* && .* rcs //;
+    my @parts = split(' ', $line);
+    while ($part = shift @parts) {
+        if ($part =~ /^-/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /\.(o|obj)$/) {
+            push(@objfiles, $part);
+        } elsif ($part =~ /\.(a|lib)$/) {
+            $libout = $part;
+            $libout =~ s/\.a$//;
+        } else {
+            die "Unhandled lib option @ line $lineno: $part";
+        }
+    }
+#    print "LibOut: '$libout'\nLFlags: @lflags\nOfiles: @objfiles\n";
+#    exit(1);
+    foreach (@objfiles) {
+        my $sourcefile = $_;
+        $sourcefile =~ s/\.o/.c/;
+        push(@sources, $sourcefile);
+        push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}});
+        push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}});
+        push(@incpaths, @{$compile_options{"${sourcefile}_INCPATHS"}});
+    }
+    removeDuplicates();
+
+    push(@{$build_structure{"LIBS"}}, $libout);
+    @{$build_structure{"LIBS_${libout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_SOURCES",
+                                             "_OBJECTS");
+    @{$build_structure{"LIBS_${libout}_DEFINES"}} = @defines;
+    @{$build_structure{"LIBS_${libout}_INCLUDES"}} = @incpaths;
+    @{$build_structure{"LIBS_${libout}_CFLAGS"}} = @cflags;
+    @{$build_structure{"LIBS_${libout}_LFLAGS"}} = @lflags;
+    @{$build_structure{"LIBS_${libout}_SOURCES"}} = @sources;
+    @{$build_structure{"LIBS_${libout}_OBJECTS"}} = @objfiles;
+    clearCompileStep();
+}
+
+sub handleLinkLine
+{
+    my ($line, $lineno) = @_;
+    my (@objfiles, @lflags, @libs, $appout, $part);
+    my @parts = split(' ', $line);
+    shift(@parts); # ignore cmd
+    while ($part = shift @parts) {
+        if ($part =~ /^-IGNORE/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /^-[GRIMDO]/) {
+            # eat compiler flags
+        } elsif ("$part" eq "-o") {
+            $appout = shift @parts;
+        } elsif ("$part" eq "-lz") {
+            push(@libs, "zlib.lib");
+        } elsif ($part =~ /^-/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /\.(a|lib)$/) {
+            $part =~ s/\.a$/.lib/;
+            push(@libs, $part);
+        } elsif ($part =~ /\.(o|obj)$/) {
+            push(@objfiles, $part);
+        } else {
+            die "Unhandled lib option @ line $lineno: $part";
+        }
+    }
+#    print "AppOut: '$appout'\nLFlags: @lflags\nLibs  : @libs\nOfiles: @objfiles\n";
+#    exit(1);
+    foreach (@objfiles) {
+        my $sourcefile = $_;
+        $sourcefile =~ s/\.o/.c/;
+        push(@sources, $sourcefile);
+        push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}});
+        push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}});
+        push(@incpaths, @{$compile_options{"${sourcefile}_INCPATHS"}});
+    }
+    removeDuplicates();
+
+    removeDuplicates();
+    push(@{$build_structure{"APPS"}}, $appout);
+    @{$build_structure{"APPS_${appout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_LFLAGS",
+                                             "_SOURCES", "_OBJECTS", "_LIBS");
+    @{$build_structure{"APPS_${appout}_DEFINES"}} = @defines;
+    @{$build_structure{"APPS_${appout}_INCLUDES"}} = @incpaths;
+    @{$build_structure{"APPS_${appout}_CFLAGS"}} = @cflags;
+    @{$build_structure{"APPS_${appout}_LFLAGS"}} = @lflags;
+    @{$build_structure{"APPS_${appout}_SOURCES"}} = @sources;
+    @{$build_structure{"APPS_${appout}_OBJECTS"}} = @objfiles;
+    @{$build_structure{"APPS_${appout}_LIBS"}} = @libs;
+    clearCompileStep();
+}
diff --git a/contrib/buildsystems/generate b/contrib/buildsystems/generate
new file mode 100644 (file)
index 0000000..bc10f25
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+######################################################################
+# Generate buildsystem files
+#
+# This script generate buildsystem files based on the output of a
+# GNU Make --dry-run, enabling Windows users to develop Git with their
+# trusted IDE with native projects.
+#
+# Note:
+# It is not meant as *the* way of building Git with MSVC, but merely a
+# convenience. The correct way of building Git with MSVC is to use the
+# GNU Make tool to build with the maintained Makefile in the root of
+# the project. If you have the msysgit environment installed and
+# available in your current console, together with the Visual Studio
+# environment you wish to build for, all you have to do is run the
+# command:
+#     make MSVC=1
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+use File::Basename;
+use Cwd;
+
+my $git_dir = getcwd();
+$git_dir =~ s=\\=/=g;
+$git_dir = dirname($git_dir) while (!-e "$git_dir/git.c" && "$git_dir" ne "");
+die "Couldn't find Git repo" if ("$git_dir" eq "");
+exec join(" ", ("PERL5LIB=${git_dir}/contrib/buildsystems ${git_dir}/contrib/buildsystems/engine.pl", @ARGV));
diff --git a/contrib/buildsystems/parse.pl b/contrib/buildsystems/parse.pl
new file mode 100644 (file)
index 0000000..c9656ec
--- /dev/null
@@ -0,0 +1,228 @@
+#!/usr/bin/perl -w
+######################################################################
+# Do not call this script directly!
+#
+# The generate script ensures that @INC is correct before the engine
+# is executed.
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+use File::Basename;
+use Cwd;
+
+my $file = $ARGV[0];
+die "No file provided!" if !defined $file;
+
+my ($cflags, $target, $type, $line);
+
+open(F, "<$file") || die "Couldn't open file $file";
+my @data = <F>;
+close(F);
+
+while (my $text = shift @data) {
+    my $ate_next;
+    do {
+        $ate_next = 0;
+        $line++;
+        chomp $text;
+        chop $text if ($text =~ /\r$/);
+        if ($text =~ /\\$/) {
+            $text =~ s/\\$//;
+            $text .= shift @data;
+            $ate_next = 1;
+        }
+    } while($ate_next);
+
+    if($text =~ / -c /) {
+        # compilation
+        handleCompileLine($text, $line);
+
+    } elsif ($text =~ / -o /) {
+        # linking executable
+        handleLinkLine($text, $line);
+
+    } elsif ($text =~ /\.o / && $text =~ /\.a /) {
+        # libifying
+        handleLibLine($text, $line);
+
+#    } elsif ($text =~ /^cp /) {
+#        # copy file around
+#
+#    } elsif ($text =~ /^rm -f /) {
+#        # shell command
+#
+#    } elsif ($text =~ /^make[ \[]/) {
+#        # make output
+#
+#    } elsif ($text =~ /^echo /) {
+#        # echo to file
+#
+#    } elsif ($text =~ /^if /) {
+#        # shell conditional
+#
+#    } elsif ($text =~ /^tclsh /) {
+#        # translation stuff
+#
+#    } elsif ($text =~ /^umask /) {
+#        # handling boilerplates
+#
+#    } elsif ($text =~ /\$\(\:\)/) {
+#        # ignore
+#
+#    } elsif ($text =~ /^FLAGS=/) {
+#        # flags check for dependencies
+#
+#    } elsif ($text =~ /^'\/usr\/bin\/perl' -MError -e/) {
+#        # perl commands for copying files
+#
+#    } elsif ($text =~ /generate-cmdlist\.sh/) {
+#        # command for generating list of commands
+#
+#    } elsif ($text =~ /^test / && $text =~ /|| rm -f /) {
+#        # commands removing executables, if they exist
+#
+#    } elsif ($text =~ /new locations or Tcl/) {
+#        # command for detecting Tcl/Tk changes
+#
+#    } elsif ($text =~ /mkdir -p/) {
+#        # command creating path
+#
+#    } elsif ($text =~ /: no custom templates yet/) {
+#        # whatever
+
+    } else {
+#        print "Unhandled (line: $line): $text\n";
+    }
+}
+close(F);
+
+# use Data::Dumper;
+# print "Parsed build structure:\n";
+# print Dumper(%build_structure);
+
+# -------------------------------------------------------------------
+# Functions under here
+# -------------------------------------------------------------------
+my (%build_structure, @defines, @incpaths, @cflags, @sources);
+
+sub clearCompileStep
+{
+    @defines = ();
+    @incpaths = ();
+    @cflags = ();
+    @sources = ();
+}
+
+sub removeDuplicates
+{
+    my (%dupHash, $entry);
+    %dupHash = map { $_, 1 } @defines;
+    @defines = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @incpaths;
+    @incpaths = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @cflags;
+    @cflags = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @sources;
+    @sources = keys %dupHash;
+}
+
+sub handleCompileLine
+{
+    my ($line, $lineno) = @_;
+    my @parts = split(' ', $line);
+    shift(@parts); # ignore cmd
+    while (my $part = shift @parts) {
+        if ("$part" eq "-o") {
+            # ignore object file
+            shift @parts;
+        } elsif ("$part" eq "-c") {
+            # ignore compile flag
+        } elsif ("$part" eq "-c") {
+        } elsif ($part =~ /^.?-I/) {
+            push(@incpaths, $part);
+        } elsif ($part =~ /^.?-D/) {
+            push(@defines, $part);
+        } elsif ($part =~ /^-/) {
+            push(@cflags, $part);
+        } elsif ($part =~ /\.(c|cc|cpp)$/) {
+            push(@sources, $part);
+        } else {
+            die "Unhandled compiler option @ line $lineno: $part";
+        }
+    }
+    #print "Sources: @sources\nCFlags: @cflags\nDefine: @defines\nIncpat: @incpaths\n";
+    #exit(1);
+}
+
+sub handleLibLine
+{
+    my ($line, $lineno) = @_;
+    my (@objfiles, @lflags, $libout, $part);
+    # kill cmd and rm 'prefix'
+    $line =~ s/^rm -f .* && .* rcs //;
+    my @parts = split(' ', $line);
+    while ($part = shift @parts) {
+        if ($part =~ /^-/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /\.(o|obj)$/) {
+            push(@objfiles, $part);
+        } elsif ($part =~ /\.(a|lib)$/) {
+            $libout = $part;
+        } else {
+            die "Unhandled lib option @ line $lineno: $part";
+        }
+    }
+    #print "LibOut: '$libout'\nLFlags: @lflags\nOfiles: @objfiles\n";
+    #exit(1);
+    removeDuplicates();
+    push(@{$build_structure{"LIBS"}}, $libout);
+    @{$build_structure{"LIBS_${libout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_SOURCES",
+                                             "_OBJECTS");
+    @{$build_structure{"LIBS_${libout}_DEFINES"}} = @defines;
+    @{$build_structure{"LIBS_${libout}_INCLUDES"}} = @incpaths;
+    @{$build_structure{"LIBS_${libout}_CFLAGS"}} = @cflags;
+    @{$build_structure{"LIBS_${libout}_SOURCES"}} = @sources;
+    @{$build_structure{"LIBS_${libout}_OBJECTS"}} = @objfiles;
+    clearCompileStep();
+}
+
+sub handleLinkLine
+{
+    my ($line, $lineno) = @_;
+    my (@objfiles, @lflags, @libs, $appout, $part);
+    my @parts = split(' ', $line);
+    shift(@parts); # ignore cmd
+    while ($part = shift @parts) {
+        if ($part =~ /^-[GRIDO]/) {
+            # eat compiler flags
+        } elsif ("$part" eq "-o") {
+            $appout = shift @parts;
+        } elsif ($part =~ /^-/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /\.(a|lib)$/) {
+            push(@libs, $part);
+        } elsif ($part =~ /\.(o|obj)$/) {
+            push(@objfiles, $part);
+        } else {
+            die "Unhandled lib option @ line $lineno: $part";
+        }
+    }
+    #print "AppOut: '$appout'\nLFlags: @lflags\nLibs  : @libs\nOfiles: @objfiles\n";
+    #exit(1);
+    removeDuplicates();
+    push(@{$build_structure{"APPS"}}, $appout);
+    @{$build_structure{"APPS_${appout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_LFLAGS",
+                                             "_SOURCES", "_OBJECTS", "_LIBS");
+    @{$build_structure{"APPS_${appout}_DEFINES"}} = @defines;
+    @{$build_structure{"APPS_${appout}_INCLUDES"}} = @incpaths;
+    @{$build_structure{"APPS_${appout}_CFLAGS"}} = @cflags;
+    @{$build_structure{"APPS_${appout}_LFLAGS"}} = @lflags;
+    @{$build_structure{"APPS_${appout}_SOURCES"}} = @sources;
+    @{$build_structure{"APPS_${appout}_OBJECTS"}} = @objfiles;
+    @{$build_structure{"APPS_${appout}_LIBS"}} = @libs;
+    clearCompileStep();
+}
index 1683e6d7b8bc677469d52acf4d6ff187e084cf3a..d3fec329976c698d55f4873efb2826ed4471919a 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.
+#
+#       If you would like to see if there're untracked files, then you can
+#       set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
+#       untracked files, then a '%' will be shown next to the branch name.
+#
 # To submit patches:
 #
 #    *) Read Documentation/SubmittingPatches
@@ -84,43 +92,55 @@ __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")"
-               elif [ -f "$g/MERGE_HEAD" ]; then
-                       r="|MERGING"
-                       b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
-                       if [ -f "$g/BISECT_LOG" ]; then
-                               r="|BISECTING"
-                       fi
-                       if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then
-                               if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then
-                                       if [ -r "$g/HEAD" ]; then
-                                               b="$(cut -c1-7 "$g/HEAD")..."
-                                       fi
+                       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"
+                       elif [ -f "$g/BISECT_LOG" ]; then
+                               r="|BISECTING"
                        fi
+
+                       b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+
+                               b="$(
+                               case "${GIT_PS1_DESCRIBE_STYLE-}" in
+                               (contains)
+                                       git describe --contains HEAD ;;
+                               (branch)
+                                       git describe --contains --all HEAD ;;
+                               (describe)
+                                       git describe HEAD ;;
+                               (* | default)
+                                       git describe --exact-match HEAD ;;
+                               esac 2>/dev/null)" ||
+
+                               b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
+                               b="unknown"
+                               b="($b)"
+                       }
                fi
 
                local w
                local i
+               local s
+               local u
                local c
 
                if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
-                       if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then
+                       if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
                                c="BARE:"
                        else
                                b="GIT_DIR!"
@@ -138,15 +158,22 @@ __git_ps1 ()
                                        fi
                                fi
                        fi
-               fi
+                       if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
+                               git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+                       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"
+                       if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
+                          if [ -n "$(git ls-files --others --exclude-standard)" ]; then
+                             u="%"
+                          fi
                        fi
                fi
+
+               if [ -n "${1-}" ]; then
+                       printf "$1" "$c${b##refs/heads/}$w$i$s$u$r"
+               else
+                       printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r"
+               fi
        fi
 }
 
@@ -291,13 +318,9 @@ __git_remotes ()
                echo ${i#$d/remotes/}
        done
        [ "$ngoff" ] && shopt -u nullglob
-       for i in $(git --git-dir="$d" config --list); do
-               case "$i" in
-               remote.*.url=*)
-                       i="${i#remote.}"
-                       echo "${i/.url=*/}"
-                       ;;
-               esac
+       for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
+               i="${i#remote.}"
+               echo "${i/.url*/}"
        done
 }
 
@@ -473,7 +496,7 @@ __git_all_commands ()
                return
        fi
        local i IFS=" "$'\n'
-       for i in $(git help -a|egrep '^ ')
+       for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
        do
                case $i in
                *--*)             : helper pattern;;
@@ -578,11 +601,11 @@ __git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)"
 __git_aliases ()
 {
        local i IFS=$'\n'
-       for i in $(git --git-dir="$(__gitdir)" config --list); do
+       for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
                case "$i" in
                alias.*)
                        i="${i#alias.}"
-                       echo "${i/=*/}"
+                       echo "${i/ */}"
                        ;;
                esac
        done
@@ -601,8 +624,8 @@ __git_aliased_command ()
        done
 }
 
-# __git_find_subcommand requires 1 argument
-__git_find_subcommand ()
+# __git_find_on_cmdline requires 1 argument
+__git_find_on_cmdline ()
 {
        local word subcommand c=1
 
@@ -647,8 +670,9 @@ _git_am ()
        --*)
                __gitcomp "
                        --3way --committer-date-is-author-date --ignore-date
+                       --ignore-whitespace --ignore-space-change
                        --interactive --keep --no-utf8 --signoff --utf8
-                       --whitespace=
+                       --whitespace= --scissors
                        "
                return
        esac
@@ -668,6 +692,7 @@ _git_apply ()
                        --stat --numstat --summary --check --index
                        --cached --index-info --reverse --reject --unidiff-zero
                        --apply --no-add --exclude=
+                       --ignore-whitespace --ignore-space-change
                        --whitespace= --inaccurate-eof --verbose
                        "
                return
@@ -719,7 +744,7 @@ _git_bisect ()
        __git_has_doubledash && return
 
        local subcommands="start bad good skip reset visualize replay log run"
-       local subcommand="$(__git_find_subcommand "$subcommands")"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
                return
@@ -789,7 +814,21 @@ _git_checkout ()
 {
        __git_has_doubledash && return
 
-       __gitcomp "$(__git_refs)"
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --conflict=*)
+               __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+               ;;
+       --*)
+               __gitcomp "
+                       --quiet --ours --theirs --track --no-track --merge
+                       --conflict= --patch
+                       "
+               ;;
+       *)
+               __gitcomp "$(__git_refs)"
+               ;;
+       esac
 }
 
 _git_cherry ()
@@ -859,6 +898,7 @@ _git_commit ()
                __gitcomp "
                        --all --author= --signoff --verify --no-verify
                        --edit --amend --include --only --interactive
+                       --dry-run
                        "
                return
        esac
@@ -891,6 +931,8 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
                        --inter-hunk-context=
                        --patience
                        --raw
+                       --dirstat --dirstat= --dirstat-by-file
+                       --dirstat-by-file= --cumulative
 "
 
 _git_diff ()
@@ -911,7 +953,7 @@ _git_diff ()
 }
 
 __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
-                       tkdiff vimdiff gvimdiff xxdiff
+                       tkdiff vimdiff gvimdiff xxdiff araxis
 "
 
 _git_difftool ()
@@ -1020,13 +1062,15 @@ _git_grep ()
                        --extended-regexp --basic-regexp --fixed-strings
                        --files-with-matches --name-only
                        --files-without-match
+                       --max-depth
                        --count
                        --and --or --not --all-match
                        "
                return
                ;;
        esac
-       COMPREPLY=()
+
+       __gitcomp "$(__git_refs)"
 }
 
 _git_help ()
@@ -1098,7 +1142,7 @@ _git_ls_tree ()
 __git_log_common_options="
        --not --all
        --branches --tags --remotes
-       --first-parent --no-merges
+       --first-parent --merges --no-merges
        --max-count=
        --max-age= --since= --after=
        --min-age= --until= --before=
@@ -1143,19 +1187,23 @@ _git_log ()
                __gitcomp "$__git_log_date_formats" "" "${cur##--date=}"
                return
                ;;
+       --decorate=*)
+               __gitcomp "long short" "" "${cur##--decorate=}"
+               return
+               ;;
        --*)
                __gitcomp "
                        $__git_log_common_options
                        $__git_log_shortlog_options
                        $__git_log_gitk_options
                        --root --topo-order --date-order --reverse
-                       --follow
+                       --follow --full-diff
                        --abbrev-commit --abbrev=
                        --relative-date --date=
                        --pretty= --format= --oneline
                        --cherry-pick
                        --graph
-                       --decorate
+                       --decorate --decorate=
                        --walk-reflogs
                        --parents --children
                        $merge
@@ -1283,7 +1331,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 ()
 {
@@ -1322,6 +1370,36 @@ _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
+
+       git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
+       while read line
+       do
+               case "$line" in
+               *.*=*)
+                       echo "${line/=*/}"
+                       ;;
+               esac
+       done
+}
+
 _git_config ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -1353,7 +1431,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
                ;;
@@ -1388,6 +1467,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
@@ -1407,7 +1490,7 @@ _git_config ()
        branch.*.*)
                local pfx="${cur%.*}."
                cur="${cur##*.}"
-               __gitcomp "remote merge mergeoptions" "$pfx" "$cur"
+               __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur"
                return
                ;;
        branch.*)
@@ -1454,7 +1537,7 @@ _git_config ()
                cur="${cur##*.}"
                __gitcomp "
                        url proxy fetch push mirror skipDefaultUpdate
-                       receivepack uploadpack tagopt
+                       receivepack uploadpack tagopt pushurl
                        " "$pfx" "$cur"
                return
                ;;
@@ -1467,12 +1550,14 @@ _git_config ()
        url.*.*)
                local pfx="${cur%.*}."
                cur="${cur##*.}"
-               __gitcomp "insteadof" "$pfx" "$cur"
+               __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
                return
                ;;
        esac
        __gitcomp "
+               add.ignore-errors
                alias.
+               apply.ignorewhitespace
                apply.whitespace
                branch.autosetupmerge
                branch.autosetuprebase
@@ -1498,6 +1583,7 @@ _git_config ()
                color.interactive.help
                color.interactive.prompt
                color.pager
+               color.showbranch
                color.status
                color.status.added
                color.status.changed
@@ -1689,7 +1775,7 @@ _git_config ()
 _git_remote ()
 {
        local subcommands="add rename rm show prune update set-head"
-       local subcommand="$(__git_find_subcommand "$subcommands")"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
                return
@@ -1701,13 +1787,9 @@ _git_remote ()
                ;;
        update)
                local i c='' IFS=$'\n'
-               for i in $(git --git-dir="$(__gitdir)" config --list); do
-                       case "$i" in
-                       remotes.*)
-                               i="${i#remotes.}"
-                               c="$c ${i/=*/}"
-                               ;;
-                       esac
+               for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
+                       i="${i#remotes.}"
+                       c="$c ${i/ */}"
                done
                __gitcomp "$c"
                ;;
@@ -1717,6 +1799,11 @@ _git_remote ()
        esac
 }
 
+_git_replace ()
+{
+       __gitcomp "$(__git_refs)"
+}
+
 _git_reset ()
 {
        __git_has_doubledash && return
@@ -1724,7 +1811,7 @@ _git_reset ()
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --*)
-               __gitcomp "--merge --mixed --hard --soft"
+               __gitcomp "--merge --mixed --hard --soft --patch"
                return
                ;;
        esac
@@ -1792,7 +1879,7 @@ _git_show ()
                return
                ;;
        --*)
-               __gitcomp "--pretty= --format=
+               __gitcomp "--pretty= --format= --abbrev-commit --oneline
                        $__git_diff_common_options
                        "
                return
@@ -1809,7 +1896,8 @@ _git_show_branch ()
                __gitcomp "
                        --all --remotes --topo-order --current --more=
                        --list --independent --merge-base --no-name
-                       --sha1-name --topics --reflog
+                       --color --no-color
+                       --sha1-name --sparse --topics --reflog
                        "
                return
                ;;
@@ -1819,20 +1907,32 @@ _git_show_branch ()
 
 _git_stash ()
 {
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local save_opts='--keep-index --no-keep-index --quiet --patch'
        local subcommands='save list show apply clear drop pop create branch'
-       local subcommand="$(__git_find_subcommand "$subcommands")"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
-               __gitcomp "$subcommands"
+               case "$cur" in
+               --*)
+                       __gitcomp "$save_opts"
+                       ;;
+               *)
+                       if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
+                               __gitcomp "$subcommands"
+                       else
+                               COMPREPLY=()
+                       fi
+                       ;;
+               esac
        else
-               local cur="${COMP_WORDS[COMP_CWORD]}"
                case "$subcommand,$cur" in
                save,--*)
-                       __gitcomp "--keep-index"
+                       __gitcomp "$save_opts"
                        ;;
-               apply,--*)
-                       __gitcomp "--index"
+               apply,--*|pop,--*)
+                       __gitcomp "--index --quiet"
                        ;;
-               show,--*|drop,--*|pop,--*|branch,--*)
+               show,--*|drop,--*|branch,--*)
                        COMPREPLY=()
                        ;;
                show,*|apply,*|drop,*|pop,*|branch,*)
@@ -1851,7 +1951,7 @@ _git_submodule ()
        __git_has_doubledash && return
 
        local subcommands="add status init update summary foreach sync"
-       if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
+       if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
                local cur="${COMP_WORDS[COMP_CWORD]}"
                case "$cur" in
                --*)
@@ -1873,7 +1973,7 @@ _git_svn ()
                proplist show-ignore show-externals branch tag blame
                migrate
                "
-       local subcommand="$(__git_find_subcommand "$subcommands")"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
        else
@@ -2072,6 +2172,7 @@ _git ()
        push)        _git_push ;;
        rebase)      _git_rebase ;;
        remote)      _git_remote ;;
+       replace)     _git_replace ;;
        reset)       _git_reset ;;
        revert)      _git_revert ;;
        rm)          _git_rm ;;
index 4fa70c5ad47fcd717d9cbdb23a8142f89227f630..7f4c7929784f8e2d8b24b0eaf9f05a2f10c1c6eb 100644 (file)
 
 (eval-when-compile (require 'cl))                            ; to use `push', `pop'
 
+(defface git-blame-prefix-face
+  '((((background dark)) (:foreground "gray"
+                          :background "black"))
+    (((background light)) (:foreground "gray"
+                           :background "white"))
+    (t (:weight bold)))
+  "The face used for the hash prefix."
+  :group 'git-blame)
+
+(defgroup git-blame nil
+  "A minor mode showing Git blame information."
+  :group 'git
+  :link '(function-link git-blame-mode))
+
+
+(defcustom git-blame-use-colors t
+  "Use colors to indicate commits in `git-blame-mode'."
+  :type 'boolean
+  :group 'git-blame)
+
+(defcustom git-blame-prefix-format
+  "%h %20A:"
+  "The format of the prefix added to each line in `git-blame'
+mode. The format is passed to `format-spec' with the following format keys:
+
+  %h - the abbreviated hash
+  %H - the full hash
+  %a - the author name
+  %A - the author email
+  %c - the committer name
+  %C - the committer email
+  %s - the commit summary
+"
+  :group 'git-blame)
+
+(defcustom git-blame-mouseover-format
+  "%h %a %A: %s"
+  "The format of the description shown when pointing at a line in
+`git-blame' mode. The format string is passed to `format-spec'
+with the following format keys:
+
+  %h - the abbreviated hash
+  %H - the full hash
+  %a - the author name
+  %A - the author email
+  %c - the committer name
+  %C - the committer email
+  %s - the commit summary
+"
+  :group 'git-blame)
+
 
 (defun git-blame-color-scale (&rest elements)
   "Given a list, returns a list of triples formed with each
@@ -302,72 +353,69 @@ See also function `git-blame-mode'."
                (src-line (string-to-number (match-string 2)))
                (res-line (string-to-number (match-string 3)))
                (num-lines (string-to-number (match-string 4))))
-           (setq git-blame-current
-                 (if (string= hash "0000000000000000000000000000000000000000")
-                     nil
-                   (git-blame-new-commit
-                    hash src-line res-line num-lines))))
-         (delete-region (point) (match-end 0))
-         t)
-        ((looking-at "filename \\(.+\\)\n")
-         (let ((filename (match-string 1)))
-           (git-blame-add-info "filename" filename))
-         (delete-region (point) (match-end 0))
+           (delete-region (point) (match-end 0))
+           (setq git-blame-current (list (git-blame-new-commit hash)
+                                         src-line res-line num-lines)))
          t)
         ((looking-at "\\([a-z-]+\\) \\(.+\\)\n")
          (let ((key (match-string 1))
                (value (match-string 2)))
-           (git-blame-add-info key value))
-         (delete-region (point) (match-end 0))
-         t)
-        ((looking-at "boundary\n")
-         (setq git-blame-current nil)
-         (delete-region (point) (match-end 0))
+           (delete-region (point) (match-end 0))
+           (git-blame-add-info (car git-blame-current) key value)
+           (when (string= key "filename")
+             (git-blame-create-overlay (car git-blame-current)
+                                       (caddr git-blame-current)
+                                       (cadddr git-blame-current))
+             (setq git-blame-current nil)))
          t)
         (t
          nil)))
 
-(defun git-blame-new-commit (hash src-line res-line num-lines)
+(defun git-blame-new-commit (hash)
+  (with-current-buffer git-blame-file
+    (or (gethash hash git-blame-cache)
+        ;; Assign a random color to each new commit info
+        ;; Take care not to select the same color multiple times
+        (let* ((color (if git-blame-colors
+                          (git-blame-random-pop git-blame-colors)
+                        git-blame-ancient-color))
+               (info `(,hash (color . ,color))))
+          (puthash hash info git-blame-cache)
+          info))))
+
+(defun git-blame-create-overlay (info start-line num-lines)
   (save-excursion
     (set-buffer git-blame-file)
-    (let ((info (gethash hash git-blame-cache))
-          (inhibit-point-motion-hooks t)
+    (let ((inhibit-point-motion-hooks t)
           (inhibit-modification-hooks t))
-      (when (not info)
-       ;; Assign a random color to each new commit info
-       ;; Take care not to select the same color multiple times
-       (let ((color (if git-blame-colors
-                        (git-blame-random-pop git-blame-colors)
-                      git-blame-ancient-color)))
-          (setq info (list hash src-line res-line num-lines
-                           (git-describe-commit hash)
-                           (cons 'color color))))
-        (puthash hash info git-blame-cache))
-      (goto-line res-line)
-      (while (> num-lines 0)
-        (if (get-text-property (point) 'git-blame)
-            (forward-line)
-          (let* ((start (point))
-                 (end (progn (forward-line 1) (point)))
-                 (ovl (make-overlay start end)))
-            (push ovl git-blame-overlays)
-            (overlay-put ovl 'git-blame info)
-            (overlay-put ovl 'help-echo hash)
+      (goto-line start-line)
+      (let* ((start (point))
+             (end (progn (forward-line num-lines) (point)))
+             (ovl (make-overlay start end))
+             (hash (car info))
+             (spec `((?h . ,(substring hash 0 6))
+                     (?H . ,hash)
+                     (?a . ,(git-blame-get-info info 'author))
+                     (?A . ,(git-blame-get-info info 'author-mail))
+                     (?c . ,(git-blame-get-info info 'committer))
+                     (?C . ,(git-blame-get-info info 'committer-mail))
+                     (?s . ,(git-blame-get-info info 'summary)))))
+        (push ovl git-blame-overlays)
+        (overlay-put ovl 'git-blame info)
+        (overlay-put ovl 'help-echo
+                     (format-spec git-blame-mouseover-format spec))
+        (if git-blame-use-colors
             (overlay-put ovl 'face (list :background
-                                         (cdr (assq 'color (nthcdr 5 info)))))
-            ;; the point-entered property doesn't seem to work in overlays
-            ;;(overlay-put ovl 'point-entered
-            ;;             `(lambda (x y) (git-blame-identify ,hash)))
-            (let ((modified (buffer-modified-p)))
-              (put-text-property (if (= start 1) start (1- start)) (1- end)
-                                 'point-entered
-                                 `(lambda (x y) (git-blame-identify ,hash)))
-              (set-buffer-modified-p modified))))
-        (setq num-lines (1- num-lines))))))
-
-(defun git-blame-add-info (key value)
-  (if git-blame-current
-      (nconc git-blame-current (list (cons (intern key) value)))))
+                                         (cdr (assq 'color (cdr info))))))
+        (overlay-put ovl 'line-prefix
+                     (propertize (format-spec git-blame-prefix-format spec)
+                                 'face 'git-blame-prefix-face))))))
+
+(defun git-blame-add-info (info key value)
+  (nconc info (list (cons (intern key) value))))
+
+(defun git-blame-get-info (info key)
+  (cdr (assq key (cdr info))))
 
 (defun git-blame-current-commit ()
   (let ((info (get-char-property (point) 'git-blame)))
index eace9c18eb1d17075836694ce664a009f3e02038..214930a021e6ab18f855b0b4ff4bd7d53912d06b 100644 (file)
@@ -429,16 +429,19 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)."
     (git-get-string-sha1
      (git-call-process-string-display-error "write-tree"))))
 
-(defun git-commit-tree (buffer tree head)
-  "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
+(defun git-commit-tree (buffer tree parent)
+  "Create a commit and possibly update HEAD.
+Create a commit with the message in BUFFER using the tree with hash TREE.
+Use PARENT as the parent of the new commit. If PARENT is the current \"HEAD\",
+update the \"HEAD\" reference to the new commit."
   (let ((author-name (git-get-committer-name))
         (author-email (git-get-committer-email))
         (subject "commit (initial): ")
         author-date log-start log-end args coding-system-for-write)
-    (when head
+    (when parent
       (setq subject "commit: ")
       (push "-p" args)
-      (push head args))
+      (push parent args))
     (with-current-buffer buffer
       (goto-char (point-min))
       (if
@@ -474,7 +477,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)."
               (apply #'git-run-command-region
                      buffer log-start log-end env
                      "commit-tree" tree (nreverse args))))))
-      (when commit (git-update-ref "HEAD" commit head subject))
+      (when commit (git-update-ref "HEAD" commit parent subject))
       commit)))
 
 (defun git-empty-db-p ()
@@ -1043,7 +1046,7 @@ The FILES list must be sorted."
 (defun git-add-file ()
   "Add marked file(s) to the index cache."
   (interactive)
-  (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
+  (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored 'unmerged))))
     ;; FIXME: add support for directories
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
@@ -1116,15 +1119,6 @@ The FILES list must be sorted."
               (when buffer (with-current-buffer buffer (revert-buffer t t t)))))
           (git-success-message "Reverted" names))))))
 
-(defun git-resolve-file ()
-  "Resolve conflicts in marked file(s)."
-  (interactive)
-  (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
-    (when files
-      (when (apply 'git-call-process-display-error "update-index" "--" files)
-        (git-update-status-files files)
-        (git-success-message "Resolved" files)))))
-
 (defun git-remove-handled ()
   "Remove handled files from the status list."
   (interactive)
@@ -1553,7 +1547,6 @@ amended version of it."
     (define-key map "P"   'git-prev-unmerged-file)
     (define-key map "q"   'git-status-quit)
     (define-key map "r"   'git-remove-file)
-    (define-key map "R"   'git-resolve-file)
     (define-key map "t"    toggle-map)
     (define-key map "T"   'git-toggle-all-marks)
     (define-key map "u"   'git-unmark-file)
@@ -1595,7 +1588,6 @@ amended version of it."
       ("Merge"
        ["Next Unmerged File" git-next-unmerged-file t]
        ["Prev Unmerged File" git-prev-unmerged-file t]
-       ["Mark as Resolved" git-resolve-file t]
        ["Interactive Merge File" git-find-file-imerge t]
        ["Diff Against Common Base File" git-diff-file-base t]
        ["Diff Combined" git-diff-file-combined t]
index 342529db309821f461e8f77d05bc5e01c76802ec..e710219ca52af3c90c28bb90273d062a26543864 100755 (executable)
@@ -8,12 +8,10 @@
 # License: MIT <http://www.opensource.org/licenses/mit-license.php>
 #
 
-import optparse, sys, os, marshal, popen2, subprocess, shelve
-import tempfile, getopt, sha, os.path, time, platform
+import optparse, sys, os, marshal, subprocess, shelve
+import tempfile, getopt, os.path, time, platform
 import re
 
-from sets import Set;
-
 verbose = False
 
 
@@ -201,7 +199,7 @@ def isModeExec(mode):
 def isModeExecChanged(src_mode, dst_mode):
     return isModeExec(src_mode) != isModeExec(dst_mode)
 
-def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
+def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
     cmd = p4_build_cmd("-G %s" % (cmd))
     if verbose:
         sys.stderr.write("Opening pipe: %s\n" % cmd)
@@ -224,7 +222,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
     try:
         while True:
             entry = marshal.load(p4.stdout)
-            result.append(entry)
+           if cb is not None:
+               cb(entry)
+           else:
+               result.append(entry)
     except EOFError:
         pass
     exitCode = p4.wait()
@@ -861,8 +862,8 @@ class P4Sync(Command):
 
         self.usage += " //depot/path[@revRange]"
         self.silent = False
-        self.createdBranches = Set()
-        self.committedChanges = Set()
+        self.createdBranches = set()
+        self.committedChanges = set()
         self.branch = ""
         self.detectBranches = False
         self.detectLabels = False
@@ -950,10 +951,84 @@ class P4Sync(Command):
 
         return branches
 
-    ## Should move this out, doesn't use SELF.
-    def readP4Files(self, files):
+    # output one file from the P4 stream
+    # - helper for streamP4Files
+
+    def streamOneP4File(self, file, contents):
+       if file["type"] == "apple":
+           print "\nfile %s is a strange apple file that forks. Ignoring" % \
+               file['depotFile']
+           return
+
+        relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
+        if verbose:
+            sys.stderr.write("%s\n" % relPath)
+
+        mode = "644"
+        if isP4Exec(file["type"]):
+            mode = "755"
+        elif file["type"] == "symlink":
+            mode = "120000"
+            # p4 print on a symlink contains "target\n", so strip it off
+            last = contents.pop()
+            last = last[:-1]
+            contents.append(last)
+
+        if self.isWindows and file["type"].endswith("text"):
+            mangled = []
+            for data in contents:
+                data = data.replace("\r\n", "\n")
+                mangled.append(data)
+            contents = mangled
+
+        if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
+            contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents)
+        elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
+            contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents)
+
+        self.gitStream.write("M %s inline %s\n" % (mode, relPath))
+
+        # total length...
+        length = 0
+        for d in contents:
+            length = length + len(d)
+
+        self.gitStream.write("data %d\n" % length)
+        for d in contents:
+            self.gitStream.write(d)
+        self.gitStream.write("\n")
+
+    def streamOneP4Deletion(self, file):
+        relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
+        if verbose:
+            sys.stderr.write("delete %s\n" % relPath)
+        self.gitStream.write("D %s\n" % relPath)
+
+    # handle another chunk of streaming data
+    def streamP4FilesCb(self, marshalled):
+
+       if marshalled.has_key('depotFile') and self.stream_have_file_info:
+           # start of a new file - output the old one first
+           self.streamOneP4File(self.stream_file, self.stream_contents)
+           self.stream_file = {}
+           self.stream_contents = []
+           self.stream_have_file_info = False
+
+       # pick up the new file information... for the
+       # 'data' field we need to append to our array
+       for k in marshalled.keys():
+           if k == 'data':
+               self.stream_contents.append(marshalled['data'])
+           else:
+               self.stream_file[k] = marshalled[k]
+
+       self.stream_have_file_info = True
+
+    # Stream directly from "p4 files" into "git fast-import"
+    def streamP4Files(self, files):
         filesForCommit = []
         filesToRead = []
+        filesToDelete = []
 
         for f in files:
             includeFile = True
@@ -967,50 +1042,35 @@ class P4Sync(Command):
                 filesForCommit.append(f)
                 if f['action'] not in ('delete', 'purge'):
                     filesToRead.append(f)
+                else:
+                    filesToDelete.append(f)
 
-        filedata = []
-        if len(filesToRead) > 0:
-            filedata = p4CmdList('-x - print',
-                                 stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
-                                                  for f in filesToRead]),
-                                 stdin_mode='w+')
-
-            if "p4ExitCode" in filedata[0]:
-                die("Problems executing p4. Error: [%d]."
-                    % (filedata[0]['p4ExitCode']));
-
-        j = 0;
-        contents = {}
-        while j < len(filedata):
-            stat = filedata[j]
-            j += 1
-            text = ''
-            while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
-                text += filedata[j]['data']
-                del filedata[j]['data']
-                j += 1
-
-            if not stat.has_key('depotFile'):
-                sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
-                continue
+        # deleted files...
+        for f in filesToDelete:
+            self.streamOneP4Deletion(f)
 
-            if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
-                text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
-            elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
-                text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
+        if len(filesToRead) > 0:
+            self.stream_file = {}
+            self.stream_contents = []
+            self.stream_have_file_info = False
 
-            contents[stat['depotFile']] = text
+           # curry self argument
+           def streamP4FilesCbSelf(entry):
+               self.streamP4FilesCb(entry)
 
-        for f in filesForCommit:
-            path = f['path']
-            if contents.has_key(path):
-                f['data'] = contents[path]
+           p4CmdList("-x - print",
+               '\n'.join(['%s#%s' % (f['path'], f['rev'])
+                                                  for f in filesToRead]),
+               cb=streamP4FilesCbSelf)
 
-        return filesForCommit
+            # do the last chunk
+            if self.stream_file.has_key('depotFile'):
+                self.streamOneP4File(self.stream_file, self.stream_contents)
 
     def commit(self, details, files, branch, branchPrefixes, parent = ""):
         epoch = details["time"]
         author = details["user"]
+       self.branchPrefixes = branchPrefixes
 
         if self.verbose:
             print "commit into %s" % branch
@@ -1023,7 +1083,6 @@ class P4Sync(Command):
                 new_files.append (f)
             else:
                 sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
-        files = self.readP4Files(new_files)
 
         self.gitStream.write("commit %s\n" % branch)
 #        gitStream.write("mark :%s\n" % details["change"])
@@ -1051,33 +1110,7 @@ class P4Sync(Command):
                 print "parent %s" % parent
             self.gitStream.write("from %s\n" % parent)
 
-        for file in files:
-            if file["type"] == "apple":
-                print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
-                continue
-
-            relPath = self.stripRepoPath(file['path'], branchPrefixes)
-            if file["action"] in ("delete", "purge"):
-                self.gitStream.write("D %s\n" % relPath)
-            else:
-                data = file['data']
-
-                mode = "644"
-                if isP4Exec(file["type"]):
-                    mode = "755"
-                elif file["type"] == "symlink":
-                    mode = "120000"
-                    # p4 print on a symlink contains "target\n", so strip it off
-                    data = data[:-1]
-
-                if self.isWindows and file["type"].endswith("text"):
-                    data = data.replace("\r\n", "\n")
-
-                self.gitStream.write("M %s inline %s\n" % (mode, relPath))
-                self.gitStream.write("data %s\n" % len(data))
-                self.gitStream.write(data)
-                self.gitStream.write("\n")
-
+        self.streamP4Files(new_files)
         self.gitStream.write("\n")
 
         change = int(details["change"])
@@ -1627,7 +1660,7 @@ class P4Sync(Command):
 
             if len(self.changesFile) > 0:
                 output = open(self.changesFile).readlines()
-                changeSet = Set()
+                changeSet = set()
                 for line in output:
                     changeSet.add(int(line))
 
diff --git a/contrib/fast-import/import-directories.perl b/contrib/fast-import/import-directories.perl
new file mode 100755 (executable)
index 0000000..5782d80
--- /dev/null
@@ -0,0 +1,416 @@
+#!/usr/bin/perl -w
+#
+# Copyright 2008-2009 Peter Krefting <peter@softwolves.pp.se>
+#
+# ------------------------------------------------------------------------
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# ------------------------------------------------------------------------
+
+=pod
+
+=head1 NAME
+
+import-directories - Import bits and pieces to Git.
+
+=head1 SYNOPSIS
+
+B<import-directories.perl> F<configfile> F<outputfile>
+
+=head1 DESCRIPTION
+
+Script to import arbitrary projects version controlled by the "copy the
+source directory to a new location and edit it there"-version controlled
+projects into version control. Handles projects with arbitrary branching
+and version trees, taking a file describing the inputs and generating a
+file compatible with the L<git-fast-import(1)> format.
+
+=head1 CONFIGURATION FILE
+
+=head2 Format
+
+The configuration file is based on the standard I<.ini> format.
+
+ ; Comments start with semi-colons
+ [section]
+ key=value
+
+Please see below for information on how to escape special characters.
+
+=head2 Global configuration
+
+Global configuration is done in the B<[config]> section, which should be
+the first section in the file. Configuration can be changed by
+repeating configuration sections later on.
+
+ [config]
+ ; configure conversion of CRLFs. "convert" means that all CRLFs
+ ; should be converted into LFs (suitable for the core.autocrlf
+ ; setting set to true in Git). "none" means that all data is
+ ; treated as binary.
+ crlf=convert
+
+=head2 Revision configuration
+
+Each revision that is to be imported is described in three
+sections. Revisions should be defined in topological order, so
+that a revision's parent has always been defined when a new revision
+is introduced. All the sections for one revision must be defined
+before defining the next revision.
+
+Each revision is assigned a unique numerical identifier. The
+numbers do not need to be consecutive, nor monotonically
+increasing.
+
+For instance, if your configuration file contains only the two
+revisions 4711 and 42, where 4711 is the initial commit, the
+only requirement is that 4711 is completely defined before 42.
+
+=pod
+
+=head3 Revision description section
+
+A section whose section name is just an integer gives meta-data
+about the revision.
+
+ [3]
+ ; author sets the author of the revisions
+ author=Peter Krefting <peter@softwolves.pp.se>
+ ; branch sets the branch that the revision should be committed to
+ branch=master
+ ; parent describes the revision that is the parent of this commit
+ ; (optional)
+ parent=1
+ ; merges describes a revision that is merged into this commit
+ ; (optional; can be repeated)
+ merges=2
+ ; selects one file to take the timestamp from
+ ; (optional; if unspecified, the most recent file from the .files
+ ;  section is used)
+ timestamp=3/source.c
+
+=head3 Revision contents section
+
+A section whose section name is an integer followed by B<.files>
+describe all the files included in this revision. If a file that
+was available previously is not included in this revision, it will
+be removed.
+
+If an on-disk revision is incomplete, you can point to files from
+a previous revision. There are no restriction as to where the source
+files are located, nor to the names of them.
+
+ [3.files]
+ ; the key is the path inside the repository, the value is the path
+ ; as seen from the importer script.
+ source.c=ver-3.00/source.c
+ source.h=ver-2.99/source.h
+ readme.txt=ver-3.00/introduction to the project.txt
+
+File names are treated as byte strings (but please see below on
+quoting rules), and should be stored in the configuration file in
+the encoding that should be used in the generated repository.
+
+=head3 Revision commit message section
+
+A section whose section name is an integer followed by B<.message>
+gives the commit message. This section is read verbatim, up until
+the beginning of the next section. As such, a commit message may not
+contain a line that begins with an opening square bracket ("[") and
+ends with a closing square bracket ("]"), unless they are surrounded
+by whitespace or other characters.
+
+ [3.message]
+ Implement foobar.
+ ; trailing blank lines are ignored.
+
+=cut
+
+# Globals
+use strict;
+use integer;
+my $crlfmode = 0;
+my @revs;
+my (%revmap, %message, %files, %author, %branch, %parent, %merges, %time, %timesource);
+my $sectiontype = 0;
+my $rev = 0;
+my $mark = 1;
+
+# Check command line
+if ($#ARGV < 1 || $ARGV[0] =~ /^--?h/)
+{
+    exec('perldoc', $0);
+    exit 1;
+}
+
+# Open configuration
+my $config = $ARGV[0];
+open CFG, '<', $config or die "Cannot open configuration file \"$config\": ";
+
+# Open output
+my $output = $ARGV[1];
+open OUT, '>', $output or die "Cannot create output file \"$output\": ";
+binmode OUT;
+
+LINE: while (my $line = <CFG>)
+{
+       $line =~ s/\r?\n$//;
+       next LINE if $sectiontype != 4 && $line eq '';
+       next LINE if $line =~ /^;/;
+       my $oldsectiontype = $sectiontype;
+       my $oldrev = $rev;
+
+       # Sections
+       if ($line =~ m"^\[(config|(\d+)(|\.files|\.message))\]$")
+       {
+               if ($1 eq 'config')
+               {
+                       $sectiontype = 1;
+               }
+               elsif ($3 eq '')
+               {
+                       $sectiontype = 2;
+                       $rev = $2;
+                       # Create a new revision
+                       die "Duplicate rev: $line\n " if defined $revmap{$rev};
+                       print "Reading revision $rev\n";
+                       push @revs, $rev;
+                       $revmap{$rev} = $mark ++;
+                       $time{$revmap{$rev}} = 0;
+               }
+               elsif ($3 eq '.files')
+               {
+                       $sectiontype = 3;
+                       $rev = $2;
+                       die "Revision mismatch: $line\n " unless $rev == $oldrev;
+               }
+               elsif ($3 eq '.message')
+               {
+                       $sectiontype = 4;
+                       $rev = $2;
+                       die "Revision mismatch: $line\n " unless $rev == $oldrev;
+               }
+               else
+               {
+                       die "Internal parse error: $line\n ";
+               }
+               next LINE;
+       }
+
+       # Parse data
+       if ($sectiontype != 4)
+       {
+               # Key and value
+               if ($line =~ m"^\s*([^\s].*=.*[^\s])\s*$")
+               {
+                       my ($key, $value) = &parsekeyvaluepair($1);
+                       # Global configuration
+                       if (1 == $sectiontype)
+                       {
+                               if ($key eq 'crlf')
+                               {
+                                       $crlfmode = 1, next LINE if $value eq 'convert';
+                                       $crlfmode = 0, next LINE if $value eq 'none';
+                               }
+                               die "Unknown configuration option: $line\n ";
+                       }
+                       # Revision specification
+                       if (2 == $sectiontype)
+                       {
+                               my $current = $revmap{$rev};
+                               $author{$current} = $value, next LINE if $key eq 'author';
+                               $branch{$current} = $value, next LINE if $key eq 'branch';
+                               $parent{$current} = $value, next LINE if $key eq 'parent';
+                               $timesource{$current} = $value, next LINE if $key eq 'timestamp';
+                               push(@{$merges{$current}}, $value), next LINE if $key eq 'merges';
+                               die "Unknown revision option: $line\n ";
+                       }
+                       # Filespecs
+                       if (3 == $sectiontype)
+                       {
+                               # Add the file and create a marker
+                               die "File not found: $line\n " unless -f $value;
+                               my $current = $revmap{$rev};
+                               ${$files{$current}}{$key} = $mark;
+                               my $time = &fileblob($value, $crlfmode, $mark ++);
+
+                               # Update revision timestamp if more recent than other
+                               # files seen, or if this is the file we have selected
+                               # to take the time stamp from using the "timestamp"
+                               # directive.
+                               if ((defined $timesource{$current} && $timesource{$current} eq $value)
+                                   || $time > $time{$current})
+                               {
+                                       $time{$current} = $time;
+                               }
+                       }
+               }
+               else
+               {
+                       die "Parse error: $line\n ";
+               }
+       }
+       else
+       {
+               # Commit message
+               my $current = $revmap{$rev};
+               if (defined $message{$current})
+               {
+                       $message{$current} .= "\n";
+               }
+               $message{$current} .= $line;
+       }
+}
+close CFG;
+
+# Start spewing out data for git-fast-import
+foreach my $commit (@revs)
+{
+       # Progress
+       print OUT "progress Creating revision $commit\n";
+
+       # Create commit header
+       my $mark = $revmap{$commit};
+
+       # Branch and commit id
+       print OUT "commit refs/heads/", $branch{$mark}, "\nmark :", $mark, "\n";
+
+       # Author and timestamp
+       die "No timestamp defined for $commit (no files?)\n" unless defined $time{$mark};
+       print OUT "committer ", $author{$mark}, " ", $time{$mark}, " +0100\n";
+
+       # Commit message
+       die "No message defined for $commit\n" unless defined $message{$mark};
+       my $message = $message{$mark};
+       $message =~ s/\n$//; # Kill trailing empty line
+       print OUT "data ", length($message), "\n", $message, "\n";
+
+       # Parent and any merges
+       print OUT "from :", $revmap{$parent{$mark}}, "\n" if defined $parent{$mark};
+       if (defined $merges{$mark})
+       {
+               foreach my $merge (@{$merges{$mark}})
+               {
+                       print OUT "merge :", $revmap{$merge}, "\n";
+               }
+       }
+
+       # Output file marks
+       print OUT "deleteall\n"; # start from scratch
+       foreach my $file (sort keys %{$files{$mark}})
+       {
+               print OUT "M 644 :", ${$files{$mark}}{$file}, " $file\n";
+       }
+       print OUT "\n";
+}
+
+# Create one file blob
+sub fileblob
+{
+       my ($filename, $crlfmode, $mark) = @_;
+
+       # Import the file
+       print OUT "progress Importing $filename\nblob\nmark :$mark\n";
+       open FILE, '<', $filename or die "Cannot read $filename\n ";
+       binmode FILE;
+       my ($size, $mtime) = (stat(FILE))[7,9];
+       my $file;
+       read FILE, $file, $size;
+       close FILE;
+       $file =~ s/\r\n/\n/g if $crlfmode;
+       print OUT "data ", length($file), "\n", $file, "\n";
+
+       return $mtime;
+}
+
+# Parse a key=value pair
+sub parsekeyvaluepair
+{
+=pod
+
+=head2 Escaping special characters
+
+Key and value strings may be enclosed in quotes, in which case
+whitespace inside the quotes is preserved. Additionally, an equal
+sign may be included in the key by preceeding it with a backslash.
+For example:
+
+ "key1 "=value1
+ key2=" value2"
+ key\=3=value3
+ key4=value=4
+ "key5""=value5
+
+Here the first key is "key1 " (note the trailing white-space) and the
+second value is " value2" (note the leading white-space). The third
+key contains an equal sign "key=3" and so does the fourth value, which
+does not need to be escaped. The fifth key contains a trailing quote,
+which does not need to be escaped since it is inside a surrounding
+quote.
+
+=cut
+       my $pair = shift;
+
+       # Separate key and value by the first non-quoted equal sign
+       my ($key, $value);
+       if ($pair =~ /^(.*[^\\])=(.*)$/)
+       {
+               ($key, $value) = ($1, $2)
+       }
+       else
+       {
+               die "Parse error: $pair\n ";
+       }
+
+       # Unquote and unescape the key and value separately
+       return (&unescape($key), &unescape($value));
+}
+
+# Unquote and unescape
+sub unescape
+{
+       my $string = shift;
+
+       # First remove enclosing quotes. Backslash before the trailing
+       # quote leaves both.
+       if ($string =~ /^"(.*[^\\])"$/)
+       {
+               $string = $1;
+       }
+
+       # Second remove any backslashes inside the unquoted string.
+       # For later: Handle special sequences like \t ?
+       $string =~ s/\\(.)/$1/g;
+
+       return $string;
+}
+
+__END__
+
+=pod
+
+=head1 EXAMPLES
+
+B<import-directories.perl> F<project.import>
+
+=head1 AUTHOR
+
+Copyright 2008-2009 Peter Krefting E<lt>peter@softwolves.pp.se>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation.
+
+=cut
index 6309d146e74a428520d09cbff4dc8d9a429ec001..7001862bfdf8de1c7d446c7ca17d87d5f953ca91 100755 (executable)
@@ -8,9 +8,20 @@
 ##  perl import-tars.perl *.tar.bz2
 ##  git whatchanged import-tars
 ##
+## Use --metainfo to specify the extension for a meta data file, where
+## import-tars can read the commit message and optionally author and
+## committer information.
+##
+##  echo 'This is the commit message' > myfile.tar.bz2.msg
+##  perl import-tars.perl --metainfo=msg myfile.tar.bz2
 
 use strict;
-die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV;
+use Getopt::Long;
+
+my $metaext = '';
+
+die "usage: import-tars [--metainfo=extension] *.tar.{gz,bz2,Z}\n"
+       unless GetOptions('metainfo=s' => \$metaext) && @ARGV;
 
 my $branch_name = 'import-tars';
 my $branch_ref = "refs/heads/$branch_name";
@@ -82,10 +93,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";
 
@@ -103,12 +120,43 @@ foreach my $tar_file (@ARGV)
                $have_top_dir = 0 if $top_dir ne $1;
        }
 
+       my $commit_msg = "Imported from $tar_file.";
+       my $this_committer_name = $committer_name;
+       my $this_committer_email = $committer_email;
+       my $this_author_name = $author_name;
+       my $this_author_email = $author_email;
+       if ($metaext ne '') {
+               # Optionally read a commit message from <filename.tar>.msg
+               # Add a line on the form "Committer: name <e-mail>" to override
+               # the committer and "Author: name <e-mail>" to override the
+               # author for this tar ball.
+               if (open MSG, '<', "${tar_file}.${metaext}") {
+                       my $header_done = 0;
+                       $commit_msg = '';
+                       while (<MSG>) {
+                               if (!$header_done && /^Committer:\s+([^<>]*)\s+<(.*)>\s*$/i) {
+                                       $this_committer_name = $1;
+                                       $this_committer_email = $2;
+                               } elsif (!$header_done && /^Author:\s+([^<>]*)\s+<(.*)>\s*$/i) {
+                                       $this_author_name = $1;
+                                       $this_author_email = $2;
+                               } elsif (!$header_done && /^$/) { # empty line ends header.
+                                       $header_done = 1;
+                               } else {
+                                       $commit_msg .= $_;
+                                       $header_done = 1;
+                               }
+                       }
+                       close MSG;
+               }
+       }
+
        print FI <<EOF;
 commit $branch_ref
-author $author_name <$author_email> $author_time +0000
-committer $committer_name <$committer_email> $commit_time +0000
+author $this_author_name <$this_author_email> $author_time +0000
+committer $this_committer_name <$this_committer_email> $commit_time +0000
 data <<END_OF_COMMIT_MESSAGE
-Imported from $tar_file.
+$commit_msg
 END_OF_COMMIT_MESSAGE
 
 deleteall
@@ -118,7 +166,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";
 
index 7b03204ed18500756ba55818f0808b52db68d048..2a6839d81ee970cb799859c7a833f8f8cb780864 100755 (executable)
@@ -20,7 +20,7 @@
 """
 
 import os, os.path, sys
-import tempfile, popen2, pickle, getopt
+import tempfile, pickle, getopt
 import re
 
 # Maps hg version -> git version
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 1816e977b7b13782003fc78a9de9b47cd3554a32..491e7141b4ea29b3cf754cbaf2656a0c3ca8c46c 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -267,7 +267,7 @@ static int filter_buffer(int fd, void *data)
 
        status = finish_command(&child_process);
        if (status)
-               error("external filter %s failed %d", params->cmd, -status);
+               error("external filter %s failed %d", params->cmd, status);
        return (write_err || status);
 }
 
diff --git a/copy.c b/copy.c
index e54d15aced7595ccb11423b0de121db9051ad1f3..a7f58fd905b31b2634f74580090ec664a640e279 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -35,6 +35,19 @@ int copy_fd(int ifd, int ofd)
        return 0;
 }
 
+static int copy_times(const char *dst, const char *src)
+{
+       struct stat st;
+       struct utimbuf times;
+       if (stat(src, &st) < 0)
+               return -1;
+       times.actime = st.st_atime;
+       times.modtime = st.st_mtime;
+       if (utime(dst, &times) < 0)
+               return -1;
+       return 0;
+}
+
 int copy_file(const char *dst, const char *src, int mode)
 {
        int fdi, fdo, status;
@@ -55,3 +68,11 @@ int copy_file(const char *dst, const char *src, int mode)
 
        return status;
 }
+
+int copy_file_with_time(const char *dst, const char *src, int mode)
+{
+       int status = copy_file(dst, src, mode);
+       if (!status)
+               return copy_times(dst, src);
+       return status;
+}
index 2ddb12a0b70da87afe6fa8a33dce08c6c8ae7f71..4d50cc5ce18c24a1dc853d3050062b864fe0b943 100644 (file)
@@ -26,7 +26,7 @@ static void flush(struct sha1file *f, void * buf, unsigned int count)
                }
                if (!ret)
                        die("sha1 file '%s' write error. Out of diskspace", f->name);
-               die("sha1 file '%s' write error (%s)", f->name, strerror(errno));
+               die_errno("sha1 file '%s' write error", f->name);
        }
 }
 
@@ -55,8 +55,7 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
                if (flags & CSUM_FSYNC)
                        fsync_or_die(f->fd, f->name);
                if (close(f->fd))
-                       die("%s: sha1 file error on close (%s)",
-                           f->name, strerror(errno));
+                       die_errno("%s: sha1 file error on close", f->name);
                fd = 0;
        } else
                fd = f->fd;
index daa4c8e8c95924c6fb446884b3ed44c454425b54..1b5ada6648da66d14dec6c399898916920582852 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1,6 +1,8 @@
 #include "cache.h"
 #include "pkt-line.h"
 #include "exec_cmd.h"
+#include "run-command.h"
+#include "strbuf.h"
 
 #include <syslog.h>
 
@@ -343,28 +345,66 @@ static int run_service(char *dir, struct daemon_service *service)
        return service->fn();
 }
 
+static void copy_to_log(int fd)
+{
+       struct strbuf line = STRBUF_INIT;
+       FILE *fp;
+
+       fp = fdopen(fd, "r");
+       if (fp == NULL) {
+               logerror("fdopen of error channel failed");
+               close(fd);
+               return;
+       }
+
+       while (strbuf_getline(&line, fp, '\n') != EOF) {
+               logerror("%s", line.buf);
+               strbuf_setlen(&line, 0);
+       }
+
+       strbuf_release(&line);
+       fclose(fp);
+}
+
+static int run_service_command(const char **argv)
+{
+       struct child_process cld;
+
+       memset(&cld, 0, sizeof(cld));
+       cld.argv = argv;
+       cld.git_cmd = 1;
+       cld.err = -1;
+       if (start_command(&cld))
+               return -1;
+
+       close(0);
+       close(1);
+
+       copy_to_log(cld.err);
+
+       return finish_command(&cld);
+}
+
 static int upload_pack(void)
 {
        /* Timeout as string */
        char timeout_buf[64];
+       const char *argv[] = { "upload-pack", "--strict", timeout_buf, ".", NULL };
 
        snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
-
-       /* git-upload-pack only ever reads stuff, so this is safe */
-       execl_git_cmd("upload-pack", "--strict", timeout_buf, ".", NULL);
-       return -1;
+       return run_service_command(argv);
 }
 
 static int upload_archive(void)
 {
-       execl_git_cmd("upload-archive", ".", NULL);
-       return -1;
+       static const char *argv[] = { "upload-archive", ".", NULL };
+       return run_service_command(argv);
 }
 
 static int receive_pack(void)
 {
-       execl_git_cmd("receive-pack", ".", NULL);
-       return -1;
+       static const char *argv[] = { "receive-pack", ".", NULL };
+       return run_service_command(argv);
 }
 
 static struct daemon_service daemon_service[] = {
@@ -406,15 +446,15 @@ static char *xstrdup_tolower(const char *str)
 }
 
 /*
- * Separate the "extra args" information as supplied by the client connection.
+ * Read the host as supplied by the client connection.
  */
-static void parse_extra_args(char *extra_args, int buflen)
+static void parse_host_arg(char *extra_args, int buflen)
 {
        char *val;
        int vallen;
        char *end = extra_args + buflen;
 
-       while (extra_args < end && *extra_args) {
+       if (extra_args < end && *extra_args) {
                saw_extended_args = 1;
                if (strncasecmp("host=", extra_args, 5) == 0) {
                        val = extra_args + 5;
@@ -436,6 +476,8 @@ static void parse_extra_args(char *extra_args, int buflen)
                        /* On to the next one */
                        extra_args = val + vallen;
                }
+               if (extra_args < end && *extra_args)
+                       die("Invalid request");
        }
 
        /*
@@ -451,7 +493,7 @@ static void parse_extra_args(char *extra_args, int buflen)
                memset(&hints, 0, sizeof(hints));
                hints.ai_flags = AI_CANONNAME;
 
-               gai = getaddrinfo(hostname, 0, &hints, &ai);
+               gai = getaddrinfo(hostname, NULL, &hints, &ai);
                if (!gai) {
                        struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
 
@@ -545,7 +587,7 @@ static int execute(struct sockaddr *addr)
        hostname = canon_hostname = ip_address = tcp_port = NULL;
 
        if (len != pktlen)
-               parse_extra_args(line + len + 1, pktlen - len - 1);
+               parse_host_arg(line + len + 1, pktlen - len - 1);
 
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
@@ -860,7 +902,7 @@ static int service_loop(int socknum, int *socklist)
                                        case ECONNABORTED:
                                                continue;
                                        default:
-                                               die("accept returned %s", strerror(errno));
+                                               die_errno("accept returned");
                                        }
                                }
                                handle(incoming, (struct sockaddr *)&ss, sslen);
@@ -876,7 +918,7 @@ static void sanitize_stdfds(void)
        while (fd != -1 && fd < 2)
                fd = dup(fd);
        if (fd == -1)
-               die("open /dev/null or dup failed: %s", strerror(errno));
+               die_errno("open /dev/null or dup failed");
        if (fd > 2)
                close(fd);
 }
@@ -887,12 +929,12 @@ static void daemonize(void)
                case 0:
                        break;
                case -1:
-                       die("fork failed: %s", strerror(errno));
+                       die_errno("fork failed");
                default:
                        exit(0);
        }
        if (setsid() == -1)
-               die("setsid failed: %s", strerror(errno));
+               die_errno("setsid failed");
        close(0);
        close(1);
        close(2);
@@ -903,9 +945,9 @@ static void store_pid(const char *path)
 {
        FILE *f = fopen(path, "w");
        if (!f)
-               die("cannot open pid file %s: %s", path, strerror(errno));
+               die_errno("cannot open pid file '%s'", path);
        if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0)
-               die("failed to write pid file %s: %s", path, strerror(errno));
+               die_errno("failed to write pid file '%s'", path);
 }
 
 static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
@@ -1105,8 +1147,7 @@ int main(int argc, char **argv)
                socklen_t slen = sizeof(ss);
 
                if (!freopen("/dev/null", "w", stderr))
-                       die("failed to redirect stderr to /dev/null: %s",
-                           strerror(errno));
+                       die_errno("failed to redirect stderr to /dev/null");
 
                if (getpeername(0, peer, &slen))
                        peer = NULL;
diff --git a/date.c b/date.c
index 409a17d46424083b62f21e7e278393cee7bfa080..5d05ef61cfb140f004702a5ed614afa755c50670 100644 (file)
--- a/date.c
+++ b/date.c
@@ -24,6 +24,8 @@ time_t tm_to_time_t(const struct tm *tm)
                return -1;
        if (month < 2 || (year + 2) % 4)
                day--;
+       if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
+               return -1;
        return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
                tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
 }
@@ -84,6 +86,67 @@ static int local_tzoffset(unsigned long time)
        return offset * eastwest;
 }
 
+const char *show_date_relative(unsigned long time, int tz,
+                              const struct timeval *now,
+                              char *timebuf,
+                              size_t timebuf_size)
+{
+       unsigned long diff;
+       if (now->tv_sec < time)
+               return "in the future";
+       diff = now->tv_sec - time;
+       if (diff < 90) {
+               snprintf(timebuf, timebuf_size, "%lu seconds ago", diff);
+               return timebuf;
+       }
+       /* Turn it into minutes */
+       diff = (diff + 30) / 60;
+       if (diff < 90) {
+               snprintf(timebuf, timebuf_size, "%lu minutes ago", diff);
+               return timebuf;
+       }
+       /* Turn it into hours */
+       diff = (diff + 30) / 60;
+       if (diff < 36) {
+               snprintf(timebuf, timebuf_size, "%lu hours ago", diff);
+               return timebuf;
+       }
+       /* We deal with number of days from here on */
+       diff = (diff + 12) / 24;
+       if (diff < 14) {
+               snprintf(timebuf, timebuf_size, "%lu days ago", diff);
+               return timebuf;
+       }
+       /* Say weeks for the past 10 weeks or so */
+       if (diff < 70) {
+               snprintf(timebuf, timebuf_size, "%lu weeks ago", (diff + 3) / 7);
+               return timebuf;
+       }
+       /* Say months for the past 12 months or so */
+       if (diff < 365) {
+               snprintf(timebuf, timebuf_size, "%lu months ago", (diff + 15) / 30);
+               return timebuf;
+       }
+       /* Give years and months for 5 years or so */
+       if (diff < 1825) {
+               unsigned long years = diff / 365;
+               unsigned long months = (diff % 365 + 15) / 30;
+               int n;
+               n = snprintf(timebuf, timebuf_size, "%lu year%s",
+                               years, (years > 1 ? "s" : ""));
+               if (months)
+                       snprintf(timebuf + n, timebuf_size - n,
+                                       ", %lu month%s ago",
+                                       months, (months > 1 ? "s" : ""));
+               else
+                       snprintf(timebuf + n, timebuf_size - n, " ago");
+               return timebuf;
+       }
+       /* Otherwise, just years. Centuries is probably overkill. */
+       snprintf(timebuf, timebuf_size, "%lu years ago", (diff + 183) / 365);
+       return timebuf;
+}
+
 const char *show_date(unsigned long time, int tz, enum date_mode mode)
 {
        struct tm *tm;
@@ -95,63 +158,10 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
        }
 
        if (mode == DATE_RELATIVE) {
-               unsigned long diff;
                struct timeval now;
                gettimeofday(&now, NULL);
-               if (now.tv_sec < time)
-                       return "in the future";
-               diff = now.tv_sec - time;
-               if (diff < 90) {
-                       snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff);
-                       return timebuf;
-               }
-               /* Turn it into minutes */
-               diff = (diff + 30) / 60;
-               if (diff < 90) {
-                       snprintf(timebuf, sizeof(timebuf), "%lu minutes ago", diff);
-                       return timebuf;
-               }
-               /* Turn it into hours */
-               diff = (diff + 30) / 60;
-               if (diff < 36) {
-                       snprintf(timebuf, sizeof(timebuf), "%lu hours ago", diff);
-                       return timebuf;
-               }
-               /* We deal with number of days from here on */
-               diff = (diff + 12) / 24;
-               if (diff < 14) {
-                       snprintf(timebuf, sizeof(timebuf), "%lu days ago", diff);
-                       return timebuf;
-               }
-               /* Say weeks for the past 10 weeks or so */
-               if (diff < 70) {
-                       snprintf(timebuf, sizeof(timebuf), "%lu weeks ago", (diff + 3) / 7);
-                       return timebuf;
-               }
-               /* Say months for the past 12 months or so */
-               if (diff < 360) {
-                       snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30);
-                       return timebuf;
-               }
-               /* Give years and months for 5 years or so */
-               if (diff < 1825) {
-                       unsigned long years = (diff + 183) / 365;
-                       unsigned long months = (diff % 365 + 15) / 30;
-                       int n;
-                       n = snprintf(timebuf, sizeof(timebuf), "%lu year%s",
-                                       years, (years > 1 ? "s" : ""));
-                       if (months)
-                               snprintf(timebuf + n, sizeof(timebuf) - n,
-                                       ", %lu month%s ago",
-                                       months, (months > 1 ? "s" : ""));
-                       else
-                               snprintf(timebuf + n, sizeof(timebuf) - n,
-                                       " ago");
-                       return timebuf;
-               }
-               /* Otherwise, just years. Centuries is probably overkill. */
-               snprintf(timebuf, sizeof(timebuf), "%lu years ago", (diff + 183) / 365);
-               return timebuf;
+               return show_date_relative(time, tz, &now,
+                                         timebuf, sizeof(timebuf));
        }
 
        if (mode == DATE_LOCAL)
@@ -425,13 +435,19 @@ static int match_multi_number(unsigned long num, char c, const char *date, char
        return end - date;
 }
 
-/* Have we filled in any part of the time/date yet? */
+/*
+ * Have we filled in any part of the time/date yet?
+ * We just do a binary 'and' to see if the sign bit
+ * is set in all the values.
+ */
 static inline int nodate(struct tm *tm)
 {
-       return tm->tm_year < 0 &&
-               tm->tm_mon < 0 &&
-               tm->tm_mday < 0 &&
-               !(tm->tm_hour | tm->tm_min | tm->tm_sec);
+       return (tm->tm_year &
+               tm->tm_mon &
+               tm->tm_mday &
+               tm->tm_hour &
+               tm->tm_min &
+               tm->tm_sec) < 0;
 }
 
 /*
@@ -525,11 +541,8 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
                }
        }
 
-       if (num > 0 && num < 32) {
-               tm->tm_mday = num;
-       } else if (num > 0 && num < 13) {
+       if (num > 0 && num < 13 && tm->tm_mon < 0)
                tm->tm_mon = num-1;
-       }
 
        return n;
 }
@@ -583,6 +596,9 @@ int parse_date(const char *date, char *result, int maxlen)
        tm.tm_mon = -1;
        tm.tm_mday = -1;
        tm.tm_isdst = -1;
+       tm.tm_hour = -1;
+       tm.tm_min = -1;
+       tm.tm_sec = -1;
        offset = -1;
        tm_gmt = 0;
 
@@ -657,42 +673,59 @@ void datestamp(char *buf, int bufsize)
        date_string(now, offset, buf, bufsize);
 }
 
-static void update_tm(struct tm *tm, unsigned long sec)
+/*
+ * Relative time update (eg "2 days ago").  If we haven't set the time
+ * yet, we need to set it from current time.
+ */
+static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec)
 {
-       time_t n = mktime(tm) - sec;
+       time_t n;
+
+       if (tm->tm_mday < 0)
+               tm->tm_mday = now->tm_mday;
+       if (tm->tm_mon < 0)
+               tm->tm_mon = now->tm_mon;
+       if (tm->tm_year < 0) {
+               tm->tm_year = now->tm_year;
+               if (tm->tm_mon > now->tm_mon)
+                       tm->tm_year--;
+       }
+
+       n = mktime(tm) - sec;
        localtime_r(&n, tm);
+       return n;
 }
 
-static void date_yesterday(struct tm *tm, int *num)
+static void date_yesterday(struct tm *tm, struct tm *now, int *num)
 {
-       update_tm(tm, 24*60*60);
+       update_tm(tm, now, 24*60*60);
 }
 
-static void date_time(struct tm *tm, int hour)
+static void date_time(struct tm *tm, struct tm *now, int hour)
 {
        if (tm->tm_hour < hour)
-               date_yesterday(tm, NULL);
+               date_yesterday(tm, now, NULL);
        tm->tm_hour = hour;
        tm->tm_min = 0;
        tm->tm_sec = 0;
 }
 
-static void date_midnight(struct tm *tm, int *num)
+static void date_midnight(struct tm *tm, struct tm *now, int *num)
 {
-       date_time(tm, 0);
+       date_time(tm, now, 0);
 }
 
-static void date_noon(struct tm *tm, int *num)
+static void date_noon(struct tm *tm, struct tm *now, int *num)
 {
-       date_time(tm, 12);
+       date_time(tm, now, 12);
 }
 
-static void date_tea(struct tm *tm, int *num)
+static void date_tea(struct tm *tm, struct tm *now, int *num)
 {
-       date_time(tm, 17);
+       date_time(tm, now, 17);
 }
 
-static void date_pm(struct tm *tm, int *num)
+static void date_pm(struct tm *tm, struct tm *now, int *num)
 {
        int hour, n = *num;
        *num = 0;
@@ -706,7 +739,7 @@ static void date_pm(struct tm *tm, int *num)
        tm->tm_hour = (hour % 12) + 12;
 }
 
-static void date_am(struct tm *tm, int *num)
+static void date_am(struct tm *tm, struct tm *now, int *num)
 {
        int hour, n = *num;
        *num = 0;
@@ -720,7 +753,7 @@ static void date_am(struct tm *tm, int *num)
        tm->tm_hour = (hour % 12);
 }
 
-static void date_never(struct tm *tm, int *num)
+static void date_never(struct tm *tm, struct tm *now, int *num)
 {
        time_t n = 0;
        localtime_r(&n, tm);
@@ -728,7 +761,7 @@ static void date_never(struct tm *tm, int *num)
 
 static const struct special {
        const char *name;
-       void (*fn)(struct tm *, int *);
+       void (*fn)(struct tm *, struct tm *, int *);
 } special[] = {
        { "yesterday", date_yesterday },
        { "noon", date_noon },
@@ -757,7 +790,7 @@ static const struct typelen {
        { NULL }
 };
 
-static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
+static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num)
 {
        const struct typelen *tl;
        const struct special *s;
@@ -778,7 +811,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
        for (s = special; s->name; s++) {
                int len = strlen(s->name);
                if (match_string(date, s->name) == len) {
-                       s->fn(tm, num);
+                       s->fn(tm, now, num);
                        return end;
                }
        }
@@ -800,7 +833,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
        while (tl->type) {
                int len = strlen(tl->type);
                if (match_string(date, tl->type) >= len-1) {
-                       update_tm(tm, tl->length * *num);
+                       update_tm(tm, now, tl->length * *num);
                        *num = 0;
                        return end;
                }
@@ -818,13 +851,15 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
                                n++;
                        diff += 7*n;
 
-                       update_tm(tm, diff * 24 * 60 * 60);
+                       update_tm(tm, now, diff * 24 * 60 * 60);
                        return end;
                }
        }
 
        if (match_string(date, "months") >= 5) {
-               int n = tm->tm_mon - *num;
+               int n;
+               update_tm(tm, now, 0); /* fill in date fields if needed */
+               n = tm->tm_mon - *num;
                *num = 0;
                while (n < 0) {
                        n += 12;
@@ -835,6 +870,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
        }
 
        if (match_string(date, "years") >= 4) {
+               update_tm(tm, now, 0); /* fill in date fields if needed */
                tm->tm_year -= *num;
                *num = 0;
                return end;
@@ -866,36 +902,82 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
        return end;
 }
 
-unsigned long approxidate(const char *date)
+/*
+ * Do we have a pending number at the end, or when
+ * we see a new one? Let's assume it's a month day,
+ * as in "Dec 6, 1992"
+ */
+static void pending_number(struct tm *tm, int *num)
+{
+       int number = *num;
+
+       if (number) {
+               *num = 0;
+               if (tm->tm_mday < 0 && number < 32)
+                       tm->tm_mday = number;
+               else if (tm->tm_mon < 0 && number < 13)
+                       tm->tm_mon = number-1;
+               else if (tm->tm_year < 0) {
+                       if (number > 1969 && number < 2100)
+                               tm->tm_year = number - 1900;
+                       else if (number > 69 && number < 100)
+                               tm->tm_year = number;
+                       else if (number < 38)
+                               tm->tm_year = 100 + number;
+                       /* We screw up for number = 00 ? */
+               }
+       }
+}
+
+static unsigned long approxidate_str(const char *date, const struct timeval *tv)
 {
        int number = 0;
        struct tm tm, now;
-       struct timeval tv;
        time_t time_sec;
-       char buffer[50];
-
-       if (parse_date(date, buffer, sizeof(buffer)) > 0)
-               return strtoul(buffer, NULL, 10);
 
-       gettimeofday(&tv, NULL);
-       time_sec = tv.tv_sec;
+       time_sec = tv->tv_sec;
        localtime_r(&time_sec, &tm);
        now = tm;
+
+       tm.tm_year = -1;
+       tm.tm_mon = -1;
+       tm.tm_mday = -1;
+
        for (;;) {
                unsigned char c = *date;
                if (!c)
                        break;
                date++;
                if (isdigit(c)) {
+                       pending_number(&tm, &number);
                        date = approxidate_digit(date-1, &tm, &number);
                        continue;
                }
                if (isalpha(c))
-                       date = approxidate_alpha(date-1, &tm, &number);
+                       date = approxidate_alpha(date-1, &tm, &now, &number);
        }
-       if (number > 0 && number < 32)
-               tm.tm_mday = number;
-       if (tm.tm_mon > now.tm_mon && tm.tm_year == now.tm_year)
-               tm.tm_year--;
-       return mktime(&tm);
+       pending_number(&tm, &number);
+       return update_tm(&tm, &now, 0);
+}
+
+unsigned long approxidate_relative(const char *date, const struct timeval *tv)
+{
+       char buffer[50];
+
+       if (parse_date(date, buffer, sizeof(buffer)) > 0)
+               return strtoul(buffer, NULL, 0);
+
+       return approxidate_str(date, tv);
+}
+
+unsigned long approxidate(const char *date)
+{
+       struct timeval tv;
+       char buffer[50];
+
+       if (parse_date(date, buffer, sizeof(buffer)) > 0)
+               return strtoul(buffer, NULL, 0);
+
+       gettimeofday(&tv, NULL);
+       return approxidate_str(date, &tv);
 }
index 82d9e221eabab53acc418d0db6327e480836a5ed..2f8a63e38881587fe29fcb72a5272ef54b9efa6e 100644 (file)
@@ -8,7 +8,9 @@
 
 static unsigned int hash_obj(const struct object *obj, unsigned int n)
 {
-       unsigned int hash = *(unsigned int *)obj->sha1;
+       unsigned int hash;
+
+       memcpy(&hash, obj->sha1, sizeof(unsigned int));
        return hash % n;
 }
 
@@ -16,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) {
@@ -68,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;
index a4e28df714b4834e5efe42fa3abb647711913d71..464ac3ffc0a45e95637e2cecdf97b3d39e5c7933 100644 (file)
@@ -4,7 +4,7 @@
  * This code was greatly inspired by parts of LibXDiff from Davide Libenzi
  * http://www.xmailserver.org/xdiff-lib.html
  *
- * Rewritten for GIT by Nicolas Pitre <nico@cam.org>, (C) 2005-2007
+ * Rewritten for GIT by Nicolas Pitre <nico@fluxnic.net>, (C) 2005-2007
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
index a310fb2ad08cfb5fac01f5519e8b95b2338b07db..0c74ef5cbe9ae0b43d8eaeb3249e4f83c695920b 100644 (file)
@@ -162,7 +162,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                if (ce_uptodate(ce))
                        continue;
 
-               changed = check_removed(ce, &st);
+               /* If CE_VALID is set, don't look at workdir for file removal */
+               changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st);
                if (changed) {
                        if (changed < 0) {
                                perror(ce->name);
@@ -214,7 +215,7 @@ static int get_stat_data(struct cache_entry *ce,
        const unsigned char *sha1 = ce->sha1;
        unsigned int mode = ce->ce_mode;
 
-       if (!cached) {
+       if (!cached && !ce_uptodate(ce)) {
                int changed;
                struct stat st;
                changed = check_removed(ce, &st);
@@ -308,22 +309,6 @@ static int show_modified(struct rev_info *revs,
        return 0;
 }
 
-/*
- * This turns all merge entries into "stage 3". That guarantees that
- * when we read in the new tree (into "stage 1"), we won't lose sight
- * of the fact that we had unmerged entries.
- */
-static void mark_merge_entries(void)
-{
-       int i;
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
-               if (!ce_stage(ce))
-                       continue;
-               ce->ce_flags |= CE_STAGEMASK;
-       }
-}
-
 /*
  * This gets a mix of an existing index and a tree, one pathname entry
  * at a time. The index entry may be a single stage-0 one, but it could
@@ -337,6 +322,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
        struct rev_info *revs = o->unpack_data;
        int match_missing, cached;
 
+       /* if the entry is not checked out, don't examine work tree */
+       cached = o->index_only || (idx && (idx->ce_flags & CE_VALID));
        /*
         * Backward compatibility wart - "diff-index -m" does
         * not mean "do not ignore merges", but "match_missing".
@@ -344,12 +331,11 @@ static void do_oneway_diff(struct unpack_trees_options *o,
         * But with the revision flag parsing, that's found in
         * "!revs->ignore_merges".
         */
-       cached = o->index_only;
        match_missing = !revs->ignore_merges;
 
        if (cached && idx && ce_stage(idx)) {
-               if (tree)
-                       diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
+               diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode,
+                            idx->sha1);
                return;
        }
 
@@ -435,8 +421,6 @@ int run_diff_index(struct rev_info *revs, int cached)
        struct unpack_trees_options opts;
        struct tree_desc t;
 
-       mark_merge_entries();
-
        ent = revs->pending.objects[0].item;
        tree_name = revs->pending.objects[0].name;
        tree = parse_tree_indirect(ent->sha1);
@@ -446,6 +430,8 @@ int run_diff_index(struct rev_info *revs, int cached)
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        opts.index_only = cached;
+       opts.diff_index_cached = (cached &&
+                                 !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER));
        opts.merge = 1;
        opts.fn = oneway_diff;
        opts.unpack_data = revs;
@@ -502,6 +488,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        opts.index_only = 1;
+       opts.diff_index_cached = !DIFF_OPT_TST(opt, FIND_COPIES_HARDER);
        opts.merge = 1;
        opts.fn = oneway_diff;
        opts.unpack_data = &revs;
diff --git a/diff.c b/diff.c
index 363dcb9613f5f57a2ae31ea6f803caf44954475a..b0c7e616a6dc41cc9052cba381ea534a72759211 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -174,6 +174,175 @@ static struct diff_tempfile {
        char tmp_path[PATH_MAX];
 } diff_temp[2];
 
+typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
+
+struct emit_callback {
+       int color_diff;
+       unsigned ws_rule;
+       int blank_at_eof_in_preimage;
+       int blank_at_eof_in_postimage;
+       int lno_in_preimage;
+       int lno_in_postimage;
+       sane_truncate_fn truncate;
+       const char **label_path;
+       struct diff_words_data *diff_words;
+       int *found_changesp;
+       FILE *file;
+};
+
+static int count_lines(const char *data, int size)
+{
+       int count, ch, completely_empty = 1, nl_just_seen = 0;
+       count = 0;
+       while (0 < size--) {
+               ch = *data++;
+               if (ch == '\n') {
+                       count++;
+                       nl_just_seen = 1;
+                       completely_empty = 0;
+               }
+               else {
+                       nl_just_seen = 0;
+                       completely_empty = 0;
+               }
+       }
+       if (completely_empty)
+               return 0;
+       if (!nl_just_seen)
+               count++; /* no trailing newline */
+       return count;
+}
+
+static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+{
+       if (!DIFF_FILE_VALID(one)) {
+               mf->ptr = (char *)""; /* does not matter */
+               mf->size = 0;
+               return 0;
+       }
+       else if (diff_populate_filespec(one, 0))
+               return -1;
+
+       mf->ptr = one->data;
+       mf->size = one->size;
+       return 0;
+}
+
+static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
+{
+       char *ptr = mf->ptr;
+       long size = mf->size;
+       int cnt = 0;
+
+       if (!size)
+               return cnt;
+       ptr += size - 1; /* pointing at the very end */
+       if (*ptr != '\n')
+               ; /* incomplete line */
+       else
+               ptr--; /* skip the last LF */
+       while (mf->ptr < ptr) {
+               char *prev_eol;
+               for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
+                       if (*prev_eol == '\n')
+                               break;
+               if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
+                       break;
+               cnt++;
+               ptr = prev_eol - 1;
+       }
+       return cnt;
+}
+
+static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
+                              struct emit_callback *ecbdata)
+{
+       int l1, l2, at;
+       unsigned ws_rule = ecbdata->ws_rule;
+       l1 = count_trailing_blank(mf1, ws_rule);
+       l2 = count_trailing_blank(mf2, ws_rule);
+       if (l2 <= l1) {
+               ecbdata->blank_at_eof_in_preimage = 0;
+               ecbdata->blank_at_eof_in_postimage = 0;
+               return;
+       }
+       at = count_lines(mf1->ptr, mf1->size);
+       ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;
+
+       at = count_lines(mf2->ptr, mf2->size);
+       ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
+}
+
+static void emit_line_0(FILE *file, const char *set, const char *reset,
+                       int first, const char *line, int len)
+{
+       int has_trailing_newline, has_trailing_carriage_return;
+       int nofirst;
+
+       if (len == 0) {
+               has_trailing_newline = (first == '\n');
+               has_trailing_carriage_return = (!has_trailing_newline &&
+                                               (first == '\r'));
+               nofirst = has_trailing_newline || has_trailing_carriage_return;
+       } else {
+               has_trailing_newline = (len > 0 && line[len-1] == '\n');
+               if (has_trailing_newline)
+                       len--;
+               has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
+               if (has_trailing_carriage_return)
+                       len--;
+               nofirst = 0;
+       }
+
+       fputs(set, file);
+
+       if (!nofirst)
+               fputc(first, file);
+       fwrite(line, len, 1, file);
+       fputs(reset, file);
+       if (has_trailing_carriage_return)
+               fputc('\r', file);
+       if (has_trailing_newline)
+               fputc('\n', file);
+}
+
+static void emit_line(FILE *file, const char *set, const char *reset,
+                     const char *line, int len)
+{
+       emit_line_0(file, set, reset, line[0], line+1, len-1);
+}
+
+static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
+{
+       if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
+             ecbdata->blank_at_eof_in_preimage &&
+             ecbdata->blank_at_eof_in_postimage &&
+             ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
+             ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
+               return 0;
+       return ws_blank_line(line, len, ecbdata->ws_rule);
+}
+
+static void emit_add_line(const char *reset,
+                         struct emit_callback *ecbdata,
+                         const char *line, int len)
+{
+       const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+       const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
+
+       if (!*ws)
+               emit_line_0(ecbdata->file, set, reset, '+', line, len);
+       else if (new_blank_line_at_eof(ecbdata, line, len))
+               /* Blank line at EOF - paint '+' as well */
+               emit_line_0(ecbdata->file, ws, reset, '+', line, len);
+       else {
+               /* Emit just the prefix, then the rest. */
+               emit_line_0(ecbdata->file, set, reset, '+', "", 0);
+               ws_check_emit(line, len, ecbdata->ws_rule,
+                             ecbdata->file, set, reset, ws);
+       }
+}
+
 static struct diff_tempfile *claim_diff_tempfile(void) {
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
@@ -189,7 +358,7 @@ static void remove_tempfile(void)
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
                if (diff_temp[i].name == diff_temp[i].tmp_path)
-                       unlink(diff_temp[i].name);
+                       unlink_or_warn(diff_temp[i].name);
                diff_temp[i].name = NULL;
        }
 }
@@ -201,29 +370,6 @@ static void remove_tempfile_on_signal(int signo)
        raise(signo);
 }
 
-static int count_lines(const char *data, int size)
-{
-       int count, ch, completely_empty = 1, nl_just_seen = 0;
-       count = 0;
-       while (0 < size--) {
-               ch = *data++;
-               if (ch == '\n') {
-                       count++;
-                       nl_just_seen = 1;
-                       completely_empty = 0;
-               }
-               else {
-                       nl_just_seen = 0;
-                       completely_empty = 0;
-               }
-       }
-       if (completely_empty)
-               return 0;
-       if (!nl_just_seen)
-               count++; /* no trailing newline */
-       return count;
-}
-
 static void print_line_count(FILE *file, int count)
 {
        switch (count) {
@@ -239,26 +385,36 @@ static void print_line_count(FILE *file, int count)
        }
 }
 
-static void copy_file_with_prefix(FILE *file,
-                                 int prefix, const char *data, int size,
-                                 const char *set, const char *reset)
+static void emit_rewrite_lines(struct emit_callback *ecb,
+                              int prefix, const char *data, int size)
 {
-       int ch, nl_just_seen = 1;
-       while (0 < size--) {
-               ch = *data++;
-               if (nl_just_seen) {
-                       fputs(set, file);
-                       putc(prefix, file);
+       const char *endp = NULL;
+       static const char *nneof = " No newline at end of file\n";
+       const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
+       const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
+
+       while (0 < size) {
+               int len;
+
+               endp = memchr(data, '\n', size);
+               len = endp ? (endp - data + 1) : size;
+               if (prefix != '+') {
+                       ecb->lno_in_preimage++;
+                       emit_line_0(ecb->file, old, reset, '-',
+                                   data, len);
+               } else {
+                       ecb->lno_in_postimage++;
+                       emit_add_line(reset, ecb, data, len);
                }
-               if (ch == '\n') {
-                       nl_just_seen = 1;
-                       fputs(reset, file);
-               } else
-                       nl_just_seen = 0;
-               putc(ch, file);
+               size -= len;
+               data += len;
+       }
+       if (!endp) {
+               const char *plain = diff_get_color(ecb->color_diff,
+                                                  DIFF_PLAIN);
+               emit_line_0(ecb->file, plain, reset, '\\',
+                           nneof, strlen(nneof));
        }
-       if (!nl_just_seen)
-               fprintf(file, "%s\n\\ No newline at end of file\n", reset);
 }
 
 static void emit_rewrite_diff(const char *name_a,
@@ -274,13 +430,12 @@ static void emit_rewrite_diff(const char *name_a,
        const char *name_a_tab, *name_b_tab;
        const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO);
        const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO);
-       const char *old = diff_get_color(color_diff, DIFF_FILE_OLD);
-       const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
        const char *reset = diff_get_color(color_diff, DIFF_RESET);
        static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
        const char *a_prefix, *b_prefix;
        const char *data_one, *data_two;
        size_t size_one, size_two;
+       struct emit_callback ecbdata;
 
        if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
                a_prefix = o->b_prefix;
@@ -321,6 +476,22 @@ static void emit_rewrite_diff(const char *name_a,
                size_two = two->size;
        }
 
+       memset(&ecbdata, 0, sizeof(ecbdata));
+       ecbdata.color_diff = color_diff;
+       ecbdata.found_changesp = &o->found_changes;
+       ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+       ecbdata.file = o->file;
+       if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
+               mmfile_t mf1, mf2;
+               mf1.ptr = (char *)data_one;
+               mf2.ptr = (char *)data_two;
+               mf1.size = size_one;
+               mf2.size = size_two;
+               check_blank_at_eof(&mf1, &mf2, &ecbdata);
+       }
+       ecbdata.lno_in_preimage = 1;
+       ecbdata.lno_in_postimage = 1;
+
        lc_a = count_lines(data_one, size_one);
        lc_b = count_lines(data_two, size_two);
        fprintf(o->file,
@@ -332,24 +503,9 @@ static void emit_rewrite_diff(const char *name_a,
        print_line_count(o->file, lc_b);
        fprintf(o->file, " @@%s\n", reset);
        if (lc_a)
-               copy_file_with_prefix(o->file, '-', data_one, size_one, old, reset);
+               emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
        if (lc_b)
-               copy_file_with_prefix(o->file, '+', data_two, size_two, new, reset);
-}
-
-static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
-{
-       if (!DIFF_FILE_VALID(one)) {
-               mf->ptr = (char *)""; /* does not matter */
-               mf->size = 0;
-               return 0;
-       }
-       else if (diff_populate_filespec(one, 0))
-               return -1;
-
-       mf->ptr = one->data;
-       mf->size = one->size;
-       return 0;
+               emit_rewrite_lines(&ecbdata, '+', data_two, size_two);
 }
 
 struct diff_words_buffer {
@@ -529,18 +685,6 @@ static void diff_words_show(struct diff_words_data *diff_words)
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
 }
 
-typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
-
-struct emit_callback {
-       int nparents, color_diff;
-       unsigned ws_rule;
-       sane_truncate_fn truncate;
-       const char **label_path;
-       struct diff_words_data *diff_words;
-       int *found_changesp;
-       FILE *file;
-};
-
 static void free_diff_words_data(struct emit_callback *ecbdata)
 {
        if (ecbdata->diff_words) {
@@ -566,42 +710,6 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
        return "";
 }
 
-static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
-{
-       int has_trailing_newline, has_trailing_carriage_return;
-
-       has_trailing_newline = (len > 0 && line[len-1] == '\n');
-       if (has_trailing_newline)
-               len--;
-       has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
-       if (has_trailing_carriage_return)
-               len--;
-
-       fputs(set, file);
-       fwrite(line, len, 1, file);
-       fputs(reset, file);
-       if (has_trailing_carriage_return)
-               fputc('\r', file);
-       if (has_trailing_newline)
-               fputc('\n', file);
-}
-
-static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
-{
-       const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
-       const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
-
-       if (!*ws)
-               emit_line(ecbdata->file, set, reset, line, len);
-       else {
-               /* Emit just the prefix, then the rest. */
-               emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
-               ws_check_emit(line + ecbdata->nparents,
-                             len - ecbdata->nparents, ecbdata->ws_rule,
-                             ecbdata->file, set, reset, ws);
-       }
-}
-
 static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len)
 {
        const char *cp;
@@ -620,10 +728,23 @@ static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, u
        return allot - l;
 }
 
+static void find_lno(const char *line, struct emit_callback *ecbdata)
+{
+       const char *p;
+       ecbdata->lno_in_preimage = 0;
+       ecbdata->lno_in_postimage = 0;
+       p = strchr(line, '-');
+       if (!p)
+               return; /* cannot happen */
+       ecbdata->lno_in_preimage = strtol(p + 1, NULL, 10);
+       p = strchr(p, '+');
+       if (!p)
+               return; /* cannot happen */
+       ecbdata->lno_in_postimage = strtol(p + 1, NULL, 10);
+}
+
 static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
-       int i;
-       int color;
        struct emit_callback *ecbdata = priv;
        const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
        const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
@@ -650,14 +771,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                len = 1;
        }
 
-       /* This is not really necessary for now because
-        * this codepath only deals with two-way diffs.
-        */
-       for (i = 0; i < len && line[i] == '@'; i++)
-               ;
-       if (2 <= i && i < len && line[i] == ' ') {
-               ecbdata->nparents = i - 1;
+       if (line[0] == '@') {
                len = sane_truncate_line(ecbdata, line, len);
+               find_lno(line, ecbdata);
                emit_line(ecbdata->file,
                          diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
                          reset, line, len);
@@ -666,15 +782,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                return;
        }
 
-       if (len < ecbdata->nparents) {
+       if (len < 1) {
                emit_line(ecbdata->file, reset, reset, line, len);
                return;
        }
 
-       color = DIFF_PLAIN;
-       if (ecbdata->diff_words && ecbdata->nparents != 1)
-               /* fall back to normal diff */
-               free_diff_words_data(ecbdata);
        if (ecbdata->diff_words) {
                if (line[0] == '-') {
                        diff_words_append(line, len,
@@ -693,20 +805,19 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                emit_line(ecbdata->file, plain, reset, line, len);
                return;
        }
-       for (i = 0; i < ecbdata->nparents && len; i++) {
-               if (line[i] == '-')
-                       color = DIFF_FILE_OLD;
-               else if (line[i] == '+')
-                       color = DIFF_FILE_NEW;
-       }
 
-       if (color != DIFF_FILE_NEW) {
-               emit_line(ecbdata->file,
-                         diff_get_color(ecbdata->color_diff, color),
-                         reset, line, len);
-               return;
+       if (line[0] != '+') {
+               const char *color =
+                       diff_get_color(ecbdata->color_diff,
+                                      line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
+               ecbdata->lno_in_preimage++;
+               if (line[0] == ' ')
+                       ecbdata->lno_in_postimage++;
+               emit_line(ecbdata->file, color, reset, line, len);
+       } else {
+               ecbdata->lno_in_postimage++;
+               emit_add_line(reset, ecbdata, line + 1, len - 1);
        }
-       emit_add_line(reset, ecbdata, line, len);
 }
 
 static char *pprint_rename(const char *a, const char *b)
@@ -839,10 +950,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 +1066,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 +1076,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 +1098,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,11 +1106,11 @@ 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_tdata, struct diff_options *options)
+static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
 {
        int i, adds = 0, dels = 0, total_files = data->nr;
 
@@ -1212,7 +1322,6 @@ struct checkdiff_t {
        struct diff_options *o;
        unsigned ws_rule;
        unsigned status;
-       int trailing_blanks_start;
 };
 
 static int is_conflict_marker(const char *line, unsigned long len)
@@ -1256,10 +1365,6 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
        if (line[0] == '+') {
                unsigned bad;
                data->lineno++;
-               if (!ws_blank_line(line + 1, len - 1, data->ws_rule))
-                       data->trailing_blanks_start = 0;
-               else if (!data->trailing_blanks_start)
-                       data->trailing_blanks_start = data->lineno;
                if (is_conflict_marker(line + 1, len - 1)) {
                        data->status |= 1;
                        fprintf(data->o->file,
@@ -1279,14 +1384,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
                              data->o->file, set, reset, ws);
        } else if (line[0] == ' ') {
                data->lineno++;
-               data->trailing_blanks_start = 0;
        } else if (line[0] == '@') {
                char *plus = strchr(line, '+');
                if (plus)
                        data->lineno = strtol(plus, NULL, 10) - 1;
                else
                        die("invalid diff");
-               data->trailing_blanks_start = 0;
        }
 }
 
@@ -1563,6 +1666,8 @@ static void builtin_diff(const char *name_a,
                ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
                ecbdata.found_changesp = &o->found_changes;
                ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+               if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
+                       check_blank_at_eof(&mf1, &mf2, &ecbdata);
                ecbdata.file = o->file;
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
@@ -1604,6 +1709,7 @@ static void builtin_diff(const char *name_a,
                        free(mf1.ptr);
                if (textconv_two)
                        free(mf2.ptr);
+               xdiff_clear_find_func(&xecfg);
        }
 
  free_ab_and_return:
@@ -1704,11 +1810,22 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
                xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
                              &xpp, &xecfg, &ecb);
 
-               if ((data.ws_rule & WS_TRAILING_SPACE) &&
-                   data.trailing_blanks_start) {
-                       fprintf(o->file, "%s:%d: ends with blank lines.\n",
-                               data.filename, data.trailing_blanks_start);
-                       data.status = 1; /* report errors */
+               if (data.ws_rule & WS_BLANK_AT_EOF) {
+                       struct emit_callback ecbdata;
+                       int blank_at_eof;
+
+                       ecbdata.ws_rule = data.ws_rule;
+                       check_blank_at_eof(&mf1, &mf2, &ecbdata);
+                       blank_at_eof = ecbdata.blank_at_eof_in_preimage;
+
+                       if (blank_at_eof) {
+                               static char *err;
+                               if (!err)
+                                       err = whitespace_error_string(WS_BLANK_AT_EOF);
+                               fprintf(o->file, "%s:%d: %s.\n",
+                                       data.filename, blank_at_eof, err);
+                               data.status = 1; /* report errors */
+                       }
                }
        }
  free_and_return:
@@ -1965,23 +2082,33 @@ 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));
+               die_errno("unable to create temp-file");
        if (convert_to_working_tree(path,
                        (const char *)blob, (size_t)size, &buf)) {
                blob = buf.buf;
                size = buf.len;
        }
        if (write_in_full(fd, blob, size) != size)
-               die("unable to write temp-file");
+               die_errno("unable to write temp-file");
        close(fd);
        temp->name = temp->tmp_path;
        strcpy(temp->hex, sha1_to_hex(sha1));
        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,
@@ -2012,21 +2139,18 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
                                goto not_a_valid_file;
-                       die("stat(%s): %s", name, strerror(errno));
+                       die_errno("stat(%s)", name);
                }
                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)
-                               die("readlink(%s)", name);
-                       if (ret == sizeof(buf))
-                               die("symlink too long: %s", name);
-                       prep_temp_blob(name, temp, buf, ret,
+                       struct strbuf sb = STRBUF_INIT;
+                       if (strbuf_readlink(&sb, name, st.st_size) < 0)
+                               die_errno("readlink(%s)", name);
+                       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 */
@@ -2213,7 +2337,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
                                return;
                        }
                        if (lstat(one->path, &st) < 0)
-                               die("stat %s", one->path);
+                               die_errno("stat '%s'", one->path);
                        if (index_path(one->sha1, one->path, &st, 0))
                                die("cannot hash %s", one->path);
                }
@@ -2684,7 +2808,7 @@ static int parse_num(const char **cp_p)
        num = 0;
        scale = 1;
        dot = 0;
-       for(;;) {
+       for (;;) {
                ch = *cp;
                if ( !dot && ch == '.' ) {
                        scale = 1;
@@ -3590,6 +3714,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 6aae09a22e54a7b9291ed1b6acf79529c6029a49..d0999ba055367c31571b251fb34bb46ed6c7051d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -14,12 +14,11 @@ struct path_simplify {
        const char *path;
 };
 
-static int read_directory_recursive(struct dir_struct *dir,
-       const char *path, const char *base, int baselen,
+static int read_directory_recursive(struct dir_struct *dir, const char *path, int len,
        int check_only, const struct path_simplify *simplify);
-static int get_dtype(struct dirent *de, const char *path);
+static int get_dtype(struct dirent *de, const char *path, int len);
 
-int common_prefix(const char **pathspec)
+static int common_prefix(const char **pathspec)
 {
        const char *path, *slash, *next;
        int prefix;
@@ -52,6 +51,26 @@ int common_prefix(const char **pathspec)
        return prefix;
 }
 
+int fill_directory(struct dir_struct *dir, const char **pathspec)
+{
+       const char *path;
+       int len;
+
+       /*
+        * Calculate common prefix for the pathspec, and
+        * use that to optimize the directory walk
+        */
+       len = common_prefix(pathspec);
+       path = "";
+
+       if (len)
+               path = xmemdupz(*pathspec, len);
+
+       /* Read the directory and prune it */
+       read_directory(dir, path, len, pathspec);
+       return len;
+}
+
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -307,7 +326,7 @@ static int excluded_1(const char *pathname,
 
                        if (x->flags & EXC_FLAG_MUSTBEDIR) {
                                if (*dtype == DT_UNKNOWN)
-                                       *dtype = get_dtype(NULL, pathname);
+                                       *dtype = get_dtype(NULL, pathname, pathlen);
                                if (*dtype != DT_DIR)
                                        continue;
                        }
@@ -396,7 +415,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);
@@ -505,7 +524,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
        /* This is the "show_other_directories" case */
        if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
                return show_directory;
-       if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify))
+       if (!read_directory_recursive(dir, dirname, len, 1, simplify))
                return ignore_directory;
        return show_directory;
 }
@@ -547,11 +566,52 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si
        return 0;
 }
 
-static int get_dtype(struct dirent *de, const char *path)
+static int get_index_dtype(const char *path, int len)
+{
+       int pos;
+       struct cache_entry *ce;
+
+       ce = cache_name_exists(path, len, 0);
+       if (ce) {
+               if (!ce_uptodate(ce))
+                       return DT_UNKNOWN;
+               if (S_ISGITLINK(ce->ce_mode))
+                       return DT_DIR;
+               /*
+                * Nobody actually cares about the
+                * difference between DT_LNK and DT_REG
+                */
+               return DT_REG;
+       }
+
+       /* Try to look it up as a directory */
+       pos = cache_name_pos(path, len);
+       if (pos >= 0)
+               return DT_UNKNOWN;
+       pos = -pos-1;
+       while (pos < active_nr) {
+               ce = active_cache[pos++];
+               if (strncmp(ce->name, path, len))
+                       break;
+               if (ce->name[len] > '/')
+                       break;
+               if (ce->name[len] < '/')
+                       continue;
+               if (!ce_uptodate(ce))
+                       break;  /* continue? */
+               return DT_DIR;
+       }
+       return DT_UNKNOWN;
+}
+
+static int get_dtype(struct dirent *de, const char *path, int len)
 {
        int dtype = de ? DTYPE(de) : DT_UNKNOWN;
        struct stat st;
 
+       if (dtype != DT_UNKNOWN)
+               return dtype;
+       dtype = get_index_dtype(path, len);
        if (dtype != DT_UNKNOWN)
                return dtype;
        if (lstat(path, &st))
@@ -574,15 +634,15 @@ static int get_dtype(struct dirent *de, const char *path)
  * Also, we ignore the name ".git" (even if it is not a directory).
  * That likely will not change.
  */
-static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
+static int read_directory_recursive(struct dir_struct *dir, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
 {
-       DIR *fdir = opendir(path);
+       DIR *fdir = opendir(*base ? base : ".");
        int contents = 0;
 
        if (fdir) {
                struct dirent *de;
-               char fullname[PATH_MAX + 1];
-               memcpy(fullname, base, baselen);
+               char path[PATH_MAX + 1];
+               memcpy(path, base, baselen);
 
                while ((de = readdir(fdir)) != NULL) {
                        int len, dtype;
@@ -593,17 +653,18 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                continue;
                        len = strlen(de->d_name);
                        /* Ignore overly long pathnames! */
-                       if (len + baselen + 8 > sizeof(fullname))
+                       if (len + baselen + 8 > sizeof(path))
                                continue;
-                       memcpy(fullname + baselen, de->d_name, len+1);
-                       if (simplify_away(fullname, baselen + len, simplify))
+                       memcpy(path + baselen, de->d_name, len+1);
+                       len = baselen + len;
+                       if (simplify_away(path, len, simplify))
                                continue;
 
                        dtype = DTYPE(de);
-                       exclude = excluded(dir, fullname, &dtype);
+                       exclude = excluded(dir, path, &dtype);
                        if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
-                           && in_pathspec(fullname, baselen + len, simplify))
-                               dir_add_ignored(dir, fullname, baselen + len);
+                           && in_pathspec(path, len, simplify))
+                               dir_add_ignored(dir, path,len);
 
                        /*
                         * Excluded? If we don't explicitly want to show
@@ -613,7 +674,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                continue;
 
                        if (dtype == DT_UNKNOWN)
-                               dtype = get_dtype(de, fullname);
+                               dtype = get_dtype(de, path, len);
 
                        /*
                         * Do we want to see just the ignored files?
@@ -630,9 +691,9 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        default:
                                continue;
                        case DT_DIR:
-                               memcpy(fullname + baselen + len, "/", 2);
+                               memcpy(path + len, "/", 2);
                                len++;
-                               switch (treat_directory(dir, fullname, baselen + len, simplify)) {
+                               switch (treat_directory(dir, path, len, simplify)) {
                                case show_directory:
                                        if (exclude != !!(dir->flags
                                                        & DIR_SHOW_IGNORED))
@@ -640,7 +701,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                        break;
                                case recurse_into_directory:
                                        contents += read_directory_recursive(dir,
-                                               fullname, fullname, baselen + len, 0, simplify);
+                                               path, len, 0, simplify);
                                        continue;
                                case ignore_directory:
                                        continue;
@@ -654,7 +715,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        if (check_only)
                                goto exit_early;
                        else
-                               dir_add_name(dir, fullname, baselen + len);
+                               dir_add_name(dir, path, len);
                }
 exit_early:
                closedir(fdir);
@@ -717,15 +778,15 @@ static void free_simplify(struct path_simplify *simplify)
        free(simplify);
 }
 
-int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
+int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
 {
        struct path_simplify *simplify;
 
-       if (has_symlink_leading_path(path, strlen(path)))
+       if (has_symlink_leading_path(path, len))
                return dir->nr;
 
        simplify = create_simplify(pathspec);
-       read_directory_recursive(dir, path, base, baselen, 0, simplify);
+       read_directory_recursive(dir, path, len, 0, simplify);
        free_simplify(simplify);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
        qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
@@ -759,7 +820,7 @@ char *get_relative_cwd(char *buffer, int size, const char *dir)
        if (!dir)
                return NULL;
        if (!getcwd(buffer, size))
-               die("can't find the current directory: %s", strerror(errno));
+               die_errno("can't find the current directory");
 
        if (!is_absolute_path(dir))
                dir = make_absolute_path(dir);
@@ -800,12 +861,20 @@ int is_empty_dir(const char *path)
        return ret;
 }
 
-int remove_dir_recursively(struct strbuf *path, int only_empty)
+int remove_dir_recursively(struct strbuf *path, int flag)
 {
-       DIR *dir = opendir(path->buf);
+       DIR *dir;
        struct dirent *e;
        int ret = 0, original_len = path->len, len;
+       int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
+       unsigned char submodule_head[20];
+
+       if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
+           !resolve_gitlink_ref(path->buf, "HEAD", submodule_head))
+               /* Do not descend and nuke a nested git work tree. */
+               return 0;
 
+       dir = opendir(path->buf);
        if (!dir)
                return -1;
        if (path->buf[original_len - 1] != '/')
diff --git a/dir.h b/dir.h
index 541286ad1daeb66a9963288d275534ee963bd5cd..320b6a2f38b9289f910141148b5dddb4cb80815f 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -61,14 +61,13 @@ struct dir_struct {
        char basebuf[PATH_MAX];
 };
 
-extern int common_prefix(const char **pathspec);
-
 #define MATCHED_RECURSIVELY 1
 #define MATCHED_FNMATCH 2
 #define MATCHED_EXACTLY 3
 extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
 
-extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
+extern int fill_directory(struct dir_struct *dir, const char **pathspec);
+extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);
 
 extern int excluded(struct dir_struct *, const char *, int *);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
@@ -89,7 +88,10 @@ static inline int is_dot_or_dotdot(const char *name)
 extern int is_empty_dir(const char *dir);
 
 extern void setup_standard_excludes(struct dir_struct *dir);
-extern int remove_dir_recursively(struct strbuf *path, int only_empty);
+
+#define REMOVE_DIR_EMPTY_ONLY 01
+#define REMOVE_DIR_KEEP_NESTED_GIT 02
+extern int remove_dir_recursively(struct strbuf *path, int flag);
 
 /* tries to remove the path with empty directories along it, ignores ENOENT */
 extern int remove_path(const char *path);
diff --git a/entry.c b/entry.c
index 915514aa5c43d3fd39f077a79f63ac27b841d255..06d24f14c6ba9401637aebfb11659ae747796c06 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -35,9 +35,9 @@ static void create_directories(const char *path, int path_len,
                 */
                if (mkdir(buf, 0777)) {
                        if (errno == EEXIST && state->force &&
-                           !unlink(buf) && !mkdir(buf, 0777))
+                           !unlink_or_warn(buf) && !mkdir(buf, 0777))
                                continue;
-                       die("cannot create directory at %s", buf);
+                       die_errno("cannot create directory at '%s'", buf);
                }
        }
        free(buf);
@@ -51,7 +51,7 @@ static void remove_subtree(const char *path)
        char *name;
 
        if (!dir)
-               die("cannot opendir %s (%s)", path, strerror(errno));
+               die_errno("cannot opendir '%s'", path);
        strcpy(pathbuf, path);
        name = pathbuf + strlen(path);
        *name++ = '/';
@@ -61,15 +61,15 @@ static void remove_subtree(const char *path)
                        continue;
                strcpy(name, de->d_name);
                if (lstat(pathbuf, &st))
-                       die("cannot lstat %s (%s)", pathbuf, strerror(errno));
+                       die_errno("cannot lstat '%s'", pathbuf);
                if (S_ISDIR(st.st_mode))
                        remove_subtree(pathbuf);
                else if (unlink(pathbuf))
-                       die("cannot unlink %s (%s)", pathbuf, strerror(errno));
+                       die_errno("cannot unlink '%s'", pathbuf);
        }
        closedir(dir);
        if (rmdir(path))
-               die("cannot rmdir %s (%s)", path, strerror(errno));
+               die_errno("cannot rmdir '%s'", path);
 }
 
 static int create_file(const char *path, unsigned int mode)
@@ -175,6 +175,23 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
        return 0;
 }
 
+/*
+ * This is like 'lstat()', except it refuses to follow symlinks
+ * in the path, after skipping "skiplen".
+ */
+int check_path(const char *path, int len, struct stat *st, int skiplen)
+{
+       const char *slash = path + len;
+
+       while (path < slash && *slash != '/')
+               slash--;
+       if (!has_dirs_only_path(path, slash - path, skiplen)) {
+               errno = ENOENT;
+               return -1;
+       }
+       return lstat(path, st);
+}
+
 int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)
 {
        static char path[PATH_MAX + 1];
@@ -188,7 +205,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
        strcpy(path + len, ce->name);
        len += ce_namelen(ce);
 
-       if (!lstat(path, &st)) {
+       if (!check_path(path, len, &st, state->base_dir_len)) {
                unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
                if (!changed)
                        return 0;
index 801a005ef1b23ef13cfa9ece676c550fe35dedc0..5de683784039f20b55f58ef36cb04a11f6a10610 100644 (file)
@@ -26,6 +26,7 @@ const char *git_commit_encoding;
 const char *git_log_output_encoding;
 int shared_repository = PERM_UMASK;
 const char *apply_default_whitespace;
+const char *apply_default_ignorewhitespace;
 int zlib_compression_level = Z_BEST_SPEED;
 int core_compression_level;
 int core_compression_seen;
@@ -38,15 +39,17 @@ int pager_use_color = 1;
 const char *editor_program;
 const char *excludes_file;
 int auto_crlf = 0;     /* 1: both ways, -1: only when adding git objects */
+int read_replace_refs = 1;
 enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
-enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
+enum push_default_type push_default = PUSH_DEFAULT_MATCHING;
 #ifndef OBJECT_CREATION_MODE
 #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
 #endif
 enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
+int grafts_replace_parents = 1;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;
index e9d23ffb2fb3f6df141944dca7ddab0edbd1e5ee..6faaaacb68999db294d20ecbb30772223bfc1b57 100644 (file)
@@ -905,10 +905,10 @@ static char *keep_pack(char *curr_index_name)
 
        keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1);
        if (keep_fd < 0)
-               die("cannot create keep file");
+               die_errno("cannot create keep file");
        write_or_die(keep_fd, keep_msg, strlen(keep_msg));
        if (close(keep_fd))
-               die("failed to write keep file");
+               die_errno("failed to write keep file");
 
        snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
                 get_object_directory(), sha1_to_hex(pack_data->sha1));
@@ -931,7 +931,7 @@ static void unkeep_all_packs(void)
                struct packed_git *p = all_packs[k];
                snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
                         get_object_directory(), sha1_to_hex(p->sha1));
-               unlink(name);
+               unlink_or_warn(name);
        }
 }
 
@@ -981,7 +981,7 @@ static void end_packfile(void)
        }
        else {
                close(old_p->pack_fd);
-               unlink(old_p->pack_name);
+               unlink_or_warn(old_p->pack_name);
        }
        free(old_p);
 
@@ -1744,10 +1744,12 @@ static int validate_raw_date(const char *src, char *result, int maxlen)
 {
        const char *orig_src = src;
        char *endp;
+       unsigned long num;
 
        errno = 0;
 
-       strtoul(src, &endp, 10);
+       num = strtoul(src, &endp, 10);
+       /* NEEDSWORK: perhaps check for reasonable values? */
        if (errno || endp == src || *endp != ' ')
                return -1;
 
@@ -1755,8 +1757,9 @@ static int validate_raw_date(const char *src, char *result, int maxlen)
        if (*src != '-' && *src != '+')
                return -1;
 
-       strtoul(src + 1, &endp, 10);
-       if (errno || endp == src || *endp || (endp - orig_src) >= maxlen)
+       num = strtoul(src + 1, &endp, 10);
+       if (errno || endp == src + 1 || *endp || (endp - orig_src) >= maxlen ||
+           1400 < num)
                return -1;
 
        strcpy(result, orig_src);
@@ -2342,7 +2345,7 @@ static void import_marks(const char *input_file)
        char line[512];
        FILE *f = fopen(input_file, "r");
        if (!f)
-               die("cannot read %s: %s", input_file, strerror(errno));
+               die_errno("cannot read '%s'", input_file);
        while (fgets(line, sizeof(line), f)) {
                uintmax_t mark;
                char *end;
@@ -2448,7 +2451,7 @@ int main(int argc, const char **argv)
                                fclose(pack_edges);
                        pack_edges = fopen(a + 20, "a");
                        if (!pack_edges)
-                               die("Cannot open %s: %s", a + 20, strerror(errno));
+                               die_errno("Cannot open '%s'", a + 20);
                } else if (!strcmp(a, "--force"))
                        force_update = 1;
                else if (!strcmp(a, "--quiet"))
diff --git a/fsck.c b/fsck.c
index 511b82cba9977c14d6dcba5efbd6d38591d3aacc..89278c1459d36a3e2b718661ca71483522f587fd 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -229,7 +229,7 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
        struct commit_graft *graft;
        int parents = 0;
 
-       if (!commit->date)
+       if (commit->date == ULONG_MAX)
                return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
 
        if (memcmp(buffer, "tree ", 5))
index f6e536ece314316ebb281721ed27d7519577202f..69aeaf03ec65922d8a3e5e092fab3d4b6ffcb63e 100755 (executable)
@@ -72,6 +72,79 @@ sub colored {
 
 # command line options
 my $patch_mode;
+my $patch_mode_revision;
+
+sub apply_patch;
+sub apply_patch_for_checkout_commit;
+sub apply_patch_for_stash;
+
+my %patch_modes = (
+       'stage' => {
+               DIFF => 'diff-files -p',
+               APPLY => sub { apply_patch 'apply --cached', @_; },
+               APPLY_CHECK => 'apply --cached',
+               VERB => 'Stage',
+               TARGET => '',
+               PARTICIPLE => 'staging',
+               FILTER => 'file-only',
+       },
+       'stash' => {
+               DIFF => 'diff-index -p HEAD',
+               APPLY => sub { apply_patch 'apply --cached', @_; },
+               APPLY_CHECK => 'apply --cached',
+               VERB => 'Stash',
+               TARGET => '',
+               PARTICIPLE => 'stashing',
+               FILTER => undef,
+       },
+       'reset_head' => {
+               DIFF => 'diff-index -p --cached',
+               APPLY => sub { apply_patch 'apply -R --cached', @_; },
+               APPLY_CHECK => 'apply -R --cached',
+               VERB => 'Unstage',
+               TARGET => '',
+               PARTICIPLE => 'unstaging',
+               FILTER => 'index-only',
+       },
+       'reset_nothead' => {
+               DIFF => 'diff-index -R -p --cached',
+               APPLY => sub { apply_patch 'apply --cached', @_; },
+               APPLY_CHECK => 'apply --cached',
+               VERB => 'Apply',
+               TARGET => ' to index',
+               PARTICIPLE => 'applying',
+               FILTER => 'index-only',
+       },
+       'checkout_index' => {
+               DIFF => 'diff-files -p',
+               APPLY => sub { apply_patch 'apply -R', @_; },
+               APPLY_CHECK => 'apply -R',
+               VERB => 'Discard',
+               TARGET => ' from worktree',
+               PARTICIPLE => 'discarding',
+               FILTER => 'file-only',
+       },
+       'checkout_head' => {
+               DIFF => 'diff-index -p',
+               APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
+               APPLY_CHECK => 'apply -R',
+               VERB => 'Discard',
+               TARGET => ' from index and worktree',
+               PARTICIPLE => 'discarding',
+               FILTER => undef,
+       },
+       'checkout_nothead' => {
+               DIFF => 'diff-index -R -p',
+               APPLY => sub { apply_patch_for_checkout_commit '', @_ },
+               APPLY_CHECK => 'apply',
+               VERB => 'Apply',
+               TARGET => ' to index and worktree',
+               PARTICIPLE => 'applying',
+               FILTER => undef,
+       },
+);
+
+my %patch_mode_flavour = %{$patch_modes{stage}};
 
 sub run_cmd_pipe {
        if ($^O eq 'MSWin32' || $^O eq 'msys') {
@@ -186,11 +259,18 @@ sub list_modified {
                @tracked = map {
                        chomp $_;
                        unquote_path($_);
-               } run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV);
+               } run_cmd_pipe(qw(git ls-files --), @ARGV);
                return if (!@tracked);
        }
 
-       my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+       my $reference;
+       if (defined $patch_mode_revision and $patch_mode_revision ne 'HEAD') {
+               $reference = $patch_mode_revision;
+       } elsif (is_initial_commit()) {
+               $reference = get_empty_tree();
+       } else {
+               $reference = 'HEAD';
+       }
        for (run_cmd_pipe(qw(git diff-index --cached
                             --numstat --summary), $reference,
                             '--', @tracked)) {
@@ -613,12 +693,24 @@ sub add_untracked_cmd {
        print "\n";
 }
 
+sub run_git_apply {
+       my $cmd = shift;
+       my $fh;
+       open $fh, '| git ' . $cmd;
+       print $fh @_;
+       return close $fh;
+}
+
 sub parse_diff {
        my ($path) = @_;
-       my @diff = run_cmd_pipe(qw(git diff-files -p --), $path);
+       my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
+       if (defined $patch_mode_revision) {
+               push @diff_cmd, $patch_mode_revision;
+       }
+       my @diff = run_cmd_pipe("git", @diff_cmd, "--", $path);
        my @colored = ();
        if ($diff_use_color) {
-               @colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
+               @colored = run_cmd_pipe("git", @diff_cmd, qw(--color --), $path);
        }
        my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
 
@@ -767,6 +859,100 @@ sub split_hunk {
        return @split;
 }
 
+sub find_last_o_ctx {
+       my ($it) = @_;
+       my $text = $it->{TEXT};
+       my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]);
+       my $i = @{$text};
+       my $last_o_ctx = $o_ofs + $o_cnt;
+       while (0 < --$i) {
+               my $line = $text->[$i];
+               if ($line =~ /^ /) {
+                       $last_o_ctx--;
+                       next;
+               }
+               last;
+       }
+       return $last_o_ctx;
+}
+
+sub merge_hunk {
+       my ($prev, $this) = @_;
+       my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =
+           parse_hunk_header($prev->{TEXT}[0]);
+       my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =
+           parse_hunk_header($this->{TEXT}[0]);
+
+       my (@line, $i, $ofs, $o_cnt, $n_cnt);
+       $ofs = $o0_ofs;
+       $o_cnt = $n_cnt = 0;
+       for ($i = 1; $i < @{$prev->{TEXT}}; $i++) {
+               my $line = $prev->{TEXT}[$i];
+               if ($line =~ /^\+/) {
+                       $n_cnt++;
+                       push @line, $line;
+                       next;
+               }
+
+               last if ($o1_ofs <= $ofs);
+
+               $o_cnt++;
+               $ofs++;
+               if ($line =~ /^ /) {
+                       $n_cnt++;
+               }
+               push @line, $line;
+       }
+
+       for ($i = 1; $i < @{$this->{TEXT}}; $i++) {
+               my $line = $this->{TEXT}[$i];
+               if ($line =~ /^\+/) {
+                       $n_cnt++;
+                       push @line, $line;
+                       next;
+               }
+               $ofs++;
+               $o_cnt++;
+               if ($line =~ /^ /) {
+                       $n_cnt++;
+               }
+               push @line, $line;
+       }
+       my $head = ("@@ -$o0_ofs" .
+                   (($o_cnt != 1) ? ",$o_cnt" : '') .
+                   " +$n0_ofs" .
+                   (($n_cnt != 1) ? ",$n_cnt" : '') .
+                   " @@\n");
+       @{$prev->{TEXT}} = ($head, @line);
+}
+
+sub coalesce_overlapping_hunks {
+       my (@in) = @_;
+       my @out = ();
+
+       my ($last_o_ctx, $last_was_dirty);
+
+       for (grep { $_->{USE} } @in) {
+               if ($_->{TYPE} ne 'hunk') {
+                       push @out, $_;
+                       next;
+               }
+               my $text = $_->{TEXT};
+               my ($o_ofs) = parse_hunk_header($text->[0]);
+               if (defined $last_o_ctx &&
+                   $o_ofs <= $last_o_ctx &&
+                   !$_->{DIRTY} &&
+                   !$last_was_dirty) {
+                       merge_hunk($out[-1], $_);
+               }
+               else {
+                       push @out, $_;
+               }
+               $last_o_ctx = find_last_o_ctx($out[-1]);
+               $last_was_dirty = $_->{DIRTY};
+       }
+       return @out;
+}
 
 sub color_diff {
        return map {
@@ -787,6 +973,7 @@ sub edit_hunk_manually {
                or die "failed to open hunk edit file for writing: " . $!;
        print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
        print $fh @$oldtext;
+       my $participle = $patch_mode_flavour{PARTICIPLE};
        print $fh <<EOF;
 # ---
 # To remove '-' lines, make them ' ' lines (context).
@@ -794,7 +981,7 @@ sub edit_hunk_manually {
 # Lines starting with # will be removed.
 #
 # If the patch applies cleanly, the edited hunk will immediately be
-# marked for staging. If it does not apply cleanly, you will be given
+# marked for $participle. If it does not apply cleanly, you will be given
 # an opportunity to edit again. If all lines of the hunk are removed,
 # then the edit is aborted and the hunk is left unchanged.
 EOF
@@ -828,11 +1015,8 @@ EOF
 
 sub diff_applies {
        my $fh;
-       open $fh, '| git apply --recount --cached --check';
-       for my $h (@_) {
-               print $fh @{$h->{TEXT}};
-       }
-       return close $fh;
+       return run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --recount --check',
+                            map { @{$_->{TEXT}} } @_);
 }
 
 sub _restore_terminal_and_die {
@@ -878,7 +1062,8 @@ sub edit_hunk_loop {
                my $newhunk = {
                        TEXT => $text,
                        TYPE => $hunk->[$ix]->{TYPE},
-                       USE => 1
+                       USE => 1,
+                       DIRTY => 1,
                };
                if (diff_applies($head,
                                 @{$hunk}[0..$ix-1],
@@ -897,12 +1082,14 @@ sub edit_hunk_loop {
 }
 
 sub help_patch_cmd {
-       print colored $help_color, <<\EOF ;
-y - stage this hunk
-n - do not stage this hunk
-q - quit, do not stage this hunk nor any of the remaining ones
-a - stage this and all the remaining hunks in the file
-d - do not stage this hunk nor any of the remaining hunks in the file
+       my $verb = lc $patch_mode_flavour{VERB};
+       my $target = $patch_mode_flavour{TARGET};
+       print colored $help_color, <<EOF ;
+y - $verb this hunk$target
+n - do not $verb this hunk$target
+q - quit, do not $verb this hunk nor any of the remaining ones
+a - $verb this and all the remaining hunks in the file
+d - do not $verb this hunk nor any of the remaining hunks in the file
 g - select a hunk to go to
 / - search for a hunk matching the given regex
 j - leave this hunk undecided, see next undecided hunk
@@ -915,8 +1102,40 @@ e - manually edit the current hunk
 EOF
 }
 
+sub apply_patch {
+       my $cmd = shift;
+       my $ret = run_git_apply $cmd . ' --recount', @_;
+       if (!$ret) {
+               print STDERR @_;
+       }
+       return $ret;
+}
+
+sub apply_patch_for_checkout_commit {
+       my $reverse = shift;
+       my $applies_index = run_git_apply 'apply '.$reverse.' --cached --recount --check', @_;
+       my $applies_worktree = run_git_apply 'apply '.$reverse.' --recount --check', @_;
+
+       if ($applies_worktree && $applies_index) {
+               run_git_apply 'apply '.$reverse.' --cached --recount', @_;
+               run_git_apply 'apply '.$reverse.' --recount', @_;
+               return 1;
+       } elsif (!$applies_index) {
+               print colored $error_color, "The selected hunks do not apply to the index!\n";
+               if (prompt_yesno "Apply them to the worktree anyway? ") {
+                       return run_git_apply 'apply '.$reverse.' --recount', @_;
+               } else {
+                       print colored $error_color, "Nothing was applied.\n";
+                       return 0;
+               }
+       } else {
+               print STDERR @_;
+               return 0;
+       }
+}
+
 sub patch_update_cmd {
-       my @all_mods = list_modified('file-only');
+       my @all_mods = list_modified($patch_mode_flavour{FILTER});
        my @mods = grep { !($_->{BINARY}) } @all_mods;
        my @them;
 
@@ -1047,8 +1266,9 @@ sub patch_update_file {
                for (@{$hunk[$ix]{DISPLAY}}) {
                        print;
                }
-               print colored $prompt_color, 'Stage ',
-                 ($hunk[$ix]{TYPE} eq 'mode' ? 'mode change' : 'this hunk'),
+               print colored $prompt_color, $patch_mode_flavour{VERB},
+                 ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' : ' this hunk'),
+                 $patch_mode_flavour{TARGET},
                  " [y,n,q,a,d,/$other,?]? ";
                my $line = prompt_single_character;
                if ($line) {
@@ -1210,6 +1430,8 @@ sub patch_update_file {
                }
        }
 
+       @hunk = coalesce_overlapping_hunks(@hunk);
+
        my $n_lofs = 0;
        my @result = ();
        for (@hunk) {
@@ -1220,16 +1442,9 @@ sub patch_update_file {
 
        if (@result) {
                my $fh;
-
-               open $fh, '| git apply --cached --recount';
-               for (@{$head->{TEXT}}, @result) {
-                       print $fh $_;
-               }
-               if (!close $fh) {
-                       for (@{$head->{TEXT}}, @result) {
-                               print STDERR $_;
-                       }
-               }
+               my @patch = (@{$head->{TEXT}}, @result);
+               my $apply_routine = $patch_mode_flavour{APPLY};
+               &$apply_routine(@patch);
                refresh();
        }
 
@@ -1270,11 +1485,41 @@ EOF
 sub process_args {
        return unless @ARGV;
        my $arg = shift @ARGV;
-       if ($arg eq "--patch") {
-               $patch_mode = 1;
-               $arg = shift @ARGV or die "missing --";
+       if ($arg =~ /--patch(?:=(.*))?/) {
+               if (defined $1) {
+                       if ($1 eq 'reset') {
+                               $patch_mode = 'reset_head';
+                               $patch_mode_revision = 'HEAD';
+                               $arg = shift @ARGV or die "missing --";
+                               if ($arg ne '--') {
+                                       $patch_mode_revision = $arg;
+                                       $patch_mode = ($arg eq 'HEAD' ?
+                                                      'reset_head' : 'reset_nothead');
+                                       $arg = shift @ARGV or die "missing --";
+                               }
+                       } elsif ($1 eq 'checkout') {
+                               $arg = shift @ARGV or die "missing --";
+                               if ($arg eq '--') {
+                                       $patch_mode = 'checkout_index';
+                               } else {
+                                       $patch_mode_revision = $arg;
+                                       $patch_mode = ($arg eq 'HEAD' ?
+                                                      'checkout_head' : 'checkout_nothead');
+                                       $arg = shift @ARGV or die "missing --";
+                               }
+                       } elsif ($1 eq 'stage' or $1 eq 'stash') {
+                               $patch_mode = $1;
+                               $arg = shift @ARGV or die "missing --";
+                       } else {
+                               die "unknown --patch mode: $1";
+                       }
+               } else {
+                       $patch_mode = 'stage';
+                       $arg = shift @ARGV or die "missing --";
+               }
                die "invalid argument $arg, expecting --"
                    unless $arg eq "--";
+               %patch_mode_flavour = %{$patch_modes{$patch_mode}};
        }
        elsif ($arg ne "--") {
                die "invalid argument $arg, expecting --";
index 6d1848b6cce89e4953a3ca6e1b2e6e1611277a4a..c132f50da5f3416e6177dd7010e6db3660bdb073 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -11,13 +11,18 @@ git am [options] (--resolved | --skip | --abort)
 i,interactive   run interactively
 b,binary*       (historical option -- no-op)
 3,3way          allow fall back on 3way merging if needed
+q,quiet         be quiet
 s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
 k,keep          pass -k flag to git-mailinfo
+c,scissors      strip everything before a scissors line
 whitespace=     pass it through git-apply
+ignore-space-change pass it through git-apply
+ignore-whitespace pass it through git-apply
 directory=      pass it through git-apply
 C=              pass it through git-apply
 p=              pass it through git-apply
+patch-format=   format the patch(es) are in
 reject          pass it through git-apply
 resolvemsg=     override error message when patch failure occurs
 r,resolved      to be used after a patch failure
@@ -44,11 +49,7 @@ else
 fi
 
 sq () {
-       for sqarg
-       do
-               printf "%s" "$sqarg" |
-               sed -e 's/'\''/'\''\\'\'''\''/g' -e 's/.*/ '\''&'\''/'
-       done
+       git rev-parse --sq-quote "$@"
 }
 
 stop_here () {
@@ -103,7 +104,7 @@ fall_back_3way () {
     git write-tree >"$dotest/patch-merge-base+" ||
     cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge."
 
-    echo Using index info to reconstruct a base tree...
+    say Using index info to reconstruct a base tree...
     if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
        git apply --cached <"$dotest/patch"
     then
@@ -119,7 +120,7 @@ It does not apply to blobs recorded in its index."
     orig_tree=$(cat "$dotest/patch-merge-base") &&
     rm -fr "$dotest"/patch-merge-* || exit 1
 
-    echo Falling back to patching base and 3-way merge...
+    say Falling back to patching base and 3-way merge...
 
     # This is not so wrong.  Depending on which base we picked,
     # orig_tree may be wildly different from ours, but his_tree
@@ -129,6 +130,10 @@ It does not apply to blobs recorded in its index."
 
     eval GITHEAD_$his_tree='"$FIRSTLINE"'
     export GITHEAD_$his_tree
+    if test -n "$GIT_QUIET"
+    then
+           export GIT_MERGE_VERBOSITY=0
+    fi
     git-merge-recursive $orig_tree -- HEAD $his_tree || {
            git rerere
            echo Failed to merge in the changes.
@@ -137,10 +142,154 @@ It does not apply to blobs recorded in its index."
     unset GITHEAD_$his_tree
 }
 
+clean_abort () {
+       test $# = 0 || echo >&2 "$@"
+       rm -fr "$dotest"
+       exit 1
+}
+
+patch_format=
+
+check_patch_format () {
+       # early return if patch_format was set from the command line
+       if test -n "$patch_format"
+       then
+               return 0
+       fi
+
+       # we default to mbox format if input is from stdin and for
+       # directories
+       if test $# = 0 || test "x$1" = "x-" || test -d "$1"
+       then
+               patch_format=mbox
+               return 0
+       fi
+
+       # otherwise, check the first few lines of the first patch to try
+       # to detect its format
+       {
+               read l1
+               read l2
+               read l3
+               case "$l1" in
+               "From "* | "From: "*)
+                       patch_format=mbox
+                       ;;
+               '# This series applies on GIT commit'*)
+                       patch_format=stgit-series
+                       ;;
+               "# HG changeset patch")
+                       patch_format=hg
+                       ;;
+               *)
+                       # if the second line is empty and the third is
+                       # a From, Author or Date entry, this is very
+                       # likely an StGIT patch
+                       case "$l2,$l3" in
+                       ,"From: "* | ,"Author: "* | ,"Date: "*)
+                               patch_format=stgit
+                               ;;
+                       *)
+                               ;;
+                       esac
+                       ;;
+               esac
+               if test -z "$patch_format" &&
+                       test -n "$l1" &&
+                       test -n "$l2" &&
+                       test -n "$l3"
+               then
+                       # This begins with three non-empty lines.  Is this a
+                       # piece of e-mail a-la RFC2822?  Grab all the headers,
+                       # discarding the indented remainder of folded lines,
+                       # and see if it looks like that they all begin with the
+                       # header field names...
+                       sed -n -e '/^$/q' -e '/^[       ]/d' -e p "$1" |
+                       LC_ALL=C egrep -v '^[!-9;-~]+:' >/dev/null ||
+                       patch_format=mbox
+               fi
+       } < "$1" || clean_abort
+}
+
+split_patches () {
+       case "$patch_format" in
+       mbox)
+               case "$rebasing" in
+               '')
+                       keep_cr= ;;
+               ?*)
+                       keep_cr=--keep-cr ;;
+               esac
+               git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
+               clean_abort
+               ;;
+       stgit-series)
+               if test $# -ne 1
+               then
+                       clean_abort "Only one StGIT patch series can be applied at once"
+               fi
+               series_dir=`dirname "$1"`
+               series_file="$1"
+               shift
+               {
+                       set x
+                       while read filename
+                       do
+                               set "$@" "$series_dir/$filename"
+                       done
+                       # remove the safety x
+                       shift
+                       # remove the arg coming from the first-line comment
+                       shift
+               } < "$series_file" || clean_abort
+               # set the patch format appropriately
+               patch_format=stgit
+               # now handle the actual StGIT patches
+               split_patches "$@"
+               ;;
+       stgit)
+               this=0
+               for stgit in "$@"
+               do
+                       this=`expr "$this" + 1`
+                       msgnum=`printf "%0${prec}d" $this`
+                       # Perl version of StGIT parse_patch. The first nonemptyline
+                       # not starting with Author, From or Date is the
+                       # subject, and the body starts with the next nonempty
+                       # line not starting with Author, From or Date
+                       perl -ne 'BEGIN { $subject = 0 }
+                               if ($subject > 1) { print ; }
+                               elsif (/^\s+$/) { next ; }
+                               elsif (/^Author:/) { print s/Author/From/ ; }
+                               elsif (/^(From|Date)/) { print ; }
+                               elsif ($subject) {
+                                       $subject = 2 ;
+                                       print "\n" ;
+                                       print ;
+                               } else {
+                                       print "Subject: ", $_ ;
+                                       $subject = 1;
+                               }
+                       ' < "$stgit" > "$dotest/$msgnum" || clean_abort
+               done
+               echo "$this" > "$dotest/last"
+               this=
+               msgnum=
+               ;;
+       *)
+               if test -n "$parse_patch" ; then
+                       clean_abort "Patch format $patch_format is not supported."
+               else
+                       clean_abort "Patch format detection failed."
+               fi
+               ;;
+       esac
+}
+
 prec=4
 dotest="$GIT_DIR/rebase-apply"
 sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
-resolvemsg= resume=
+resolvemsg= resume= scissors=
 git_apply_opt=
 committer_date_is_author_date=
 ignore_date=
@@ -162,6 +311,10 @@ do
                utf8= ;;
        -k|--keep)
                keep=t ;;
+       -c|--scissors)
+               scissors=t ;;
+       --no-scissors)
+               scissors=f ;;
        -r|--resolved)
                resolved=t ;;
        --skip)
@@ -169,7 +322,7 @@ do
        --abort)
                abort=t ;;
        --rebasing)
-               rebasing=t threeway=t keep=t ;;
+               rebasing=t threeway=t keep=t scissors=f ;;
        -d|--dotest)
                die "-d option is no longer supported.  Do not use."
                ;;
@@ -179,12 +332,16 @@ do
                git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
        -C|-p)
                git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
-       --reject)
+       --patch-format)
+               shift ; patch_format="$1" ;;
+       --reject|--ignore-whitespace|--ignore-space-change)
                git_apply_opt="$git_apply_opt $1" ;;
        --committer-date-is-author-date)
                committer_date_is_author_date=t ;;
        --ignore-date)
                ignore_date=t ;;
+       -q|--quiet)
+               GIT_QUIET=t ;;
        --)
                shift; break ;;
        *)
@@ -278,19 +435,20 @@ else
                done
                shift
        fi
-       git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
-               rm -fr "$dotest"
-               exit 1
-       }
 
-       # -s, -u, -k, --whitespace, -3, -C and -p flags are kept
-       # for the resuming session after a patch failure.
-       # -i can and must be given when resuming.
+       check_patch_format "$@"
+
+       split_patches "$@"
+
+       # -i can and must be given when resuming; everything
+       # else is kept
        echo " $git_apply_opt" >"$dotest/apply-opt"
        echo "$threeway" >"$dotest/threeway"
        echo "$sign" >"$dotest/sign"
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
+       echo "$scissors" >"$dotest/scissors"
+       echo "$GIT_QUIET" >"$dotest/quiet"
        echo 1 >"$dotest/next"
        if test -n "$rebasing"
        then
@@ -331,6 +489,16 @@ if test "$(cat "$dotest/keep")" = t
 then
        keep=-k
 fi
+case "$(cat "$dotest/scissors")" in
+t)
+       scissors=--scissors ;;
+f)
+       scissors=--no-scissors ;;
+esac
+if test "$(cat "$dotest/quiet")" = t
+then
+       GIT_QUIET=t
+fi
 if test "$(cat "$dotest/threeway")" = t
 then
        threeway=t
@@ -356,7 +524,7 @@ fi
 
 if test "$this" -gt "$last"
 then
-       echo Nothing to do.
+       say Nothing to do.
        rm -fr "$dotest"
        exit
 fi
@@ -381,7 +549,7 @@ do
        # by the user, or the user can tell us to do so by --resolved flag.
        case "$resume" in
        '')
-               git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
+               git mailinfo $keep $scissors $utf8 "$dotest/msg" "$dotest/patch" \
                        <"$dotest/$msgnum" >"$dotest/info" ||
                        stop_here $this
 
@@ -502,11 +670,18 @@ do
                stop_here $this
        fi
 
-       printf 'Applying: %s\n' "$FIRSTLINE"
+       say "Applying: $FIRSTLINE"
 
        case "$resolved" in
        '')
-               eval 'git apply '"$git_apply_opt"' --index "$dotest/patch"'
+               # When we are allowed to fall back to 3-way later, don't give
+               # false errors during the initial attempt.
+               squelch=
+               if test "$threeway" = t
+               then
+                       squelch='>/dev/null 2>&1 '
+               fi
+               eval "git apply $squelch$git_apply_opt"' --index "$dotest/patch"'
                apply_status=$?
                ;;
        t)
@@ -538,7 +713,7 @@ do
                    # Applying the patch to an earlier tree and merging the
                    # result may have produced the same tree as ours.
                    git diff-index --quiet --cached HEAD -- && {
-                       echo No changes -- Patch already applied.
+                       say No changes -- Patch already applied.
                        go_next
                        continue
                    }
@@ -564,7 +739,7 @@ do
                        GIT_AUTHOR_DATE=
                fi
                parent=$(git rev-parse --verify -q HEAD) ||
-               echo >&2 "applying to an empty history"
+               say >&2 "applying to an empty history"
 
                if test -n "$committer_date_is_author_date"
                then
index 24712ff304af89317793fa4c54d39f4c579bb345..6f6f03966f5905c799a1a67df58e11b0a5f7564c 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() {
@@ -559,7 +405,7 @@ bisect_run () {
          exit $res
       fi
 
-      if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
+      if grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
          echo "bisect run success"
          exit 0;
       fi
index 1ac16bde5ab853098f857a64c6f0fea9f4c3437f..ef6080338480f509e720a484998c286ae1814b0a 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)
@@ -26,6 +26,7 @@
 #endif
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define bitsizeof(x)  (CHAR_BIT * sizeof(x))
 
 #ifdef __GNUC__
 #define TYPEOF(x) (__typeof__(x))
 #define TYPEOF(x)
 #endif
 
-#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits))))
+#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (bitsizeof(x) - (bits))))
 #define HAS_MULTI_BITS(i)  ((i) & ((i) - 1))  /* checks if an integer has more than 1 bit set */
 
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
 /* 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) && !defined(sgi)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif
 #define _GNU_SOURCE 1
 #define _BSD_SOURCE 1
 #define _NETBSD_SOURCE 1
+#define _SGI_SOURCE 1
+
+#ifdef WIN32 /* Both MinGW and MSVC */
+#define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
+#include <winsock2.h>
+#include <windows.h>
+#endif
 
 #include <unistd.h>
 #include <stdio.h>
 /* pull in Windows compatibility stuff */
 #include "compat/mingw.h"
 #endif /* __MINGW32__ */
+#ifdef _MSC_VER
+#include "compat/msvc.h"
+#endif
+
+#ifndef NO_LIBGEN_H
+#include <libgen.h>
+#else
+#define basename gitbasename
+extern char *gitbasename(char *);
+#endif
 
 #ifndef NO_ICONV
 #include <iconv.h>
 
 #ifdef __GNUC__
 #define NORETURN __attribute__((__noreturn__))
+#define NORETURN_PTR __attribute__((__noreturn__))
 #else
 #define NORETURN
+#define NORETURN_PTR
 #ifndef __attribute__
 #define __attribute__(x)
 #endif
 #endif
 
+#include "compat/bswap.h"
+
 /* General helper functions */
-extern void usage(const char *err) NORETURN;
-extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
+extern NORETURN void usage(const char *err);
+extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
-extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
+extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
 
 extern int prefixcmp(const char *str, const char *prefix);
 extern time_t tm_to_time_t(const struct tm *tm);
@@ -232,6 +270,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 *);
@@ -415,4 +458,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #define fstat_is_reliable() 1
 #endif
 
+/*
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Always returns the return value of unlink(2).
+ */
+int unlink_or_warn(const char *path);
+
 #endif
index 6d9f0ef0f989133422cf8c0302e63dab15a999d5..59b672213bfc36f95db089f0e13bafc1c2f2ed71 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,46 @@ 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
+           if defined $fullname{$file};
       }
     }
 }
 
-# ... 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 +288,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 +411,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 e43920296182f320dac31b5832a30844ffaef38f..1ad20ac96450d100fbce7e992ec65972a8c0a7ba 100755 (executable)
@@ -238,7 +238,9 @@ sub conn {
                }
                my $rr = ":pserver:$user\@$serv:$port$repo";
 
-               unless ($pass) {
+               if ($pass) {
+                       $pass = $self->_scramble($pass);
+               } else {
                        open(H,$ENV{'HOME'}."/.cvspass") and do {
                                # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
                                while (<H>) {
@@ -251,8 +253,8 @@ sub conn {
                                        }
                                }
                        };
+                       $pass = "A" unless $pass;
                }
-               $pass="A" unless $pass;
 
                my ($s, $rep);
                if ($proxyhost) {
@@ -484,6 +486,42 @@ sub _fetchfile {
        return $res;
 }
 
+sub _scramble {
+       my ($self, $pass) = @_;
+       my $scrambled = "A";
+
+       return $scrambled unless $pass;
+
+       my $pass_len = length($pass);
+       my @pass_arr = split("", $pass);
+       my $i;
+
+       # from cvs/src/scramble.c
+       my @shifts = (
+                 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+                16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+               114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+               111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+                41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+               125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+                36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+                58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+               225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+               199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+               174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+               207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+               192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+               227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+               182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+               243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+       );
+
+       for ($i = 0; $i < $pass_len; $i++) {
+               $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
+       }
+
+       return $scrambled;
+}
 
 package main;
 
index ab6cea3e538047c30a5d728c7a16ee3c935c070b..6dc45f5d45a44c9bbe31987c1a10e4554f037d6c 100755 (executable)
@@ -285,7 +285,7 @@ sub req_Root
        return 0;
     }
 
-    my @gitvars = `git-config -l`;
+    my @gitvars = `git config -l`;
     if ($?) {
        print "E problems executing git-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
         print "E \n";
@@ -702,7 +702,7 @@ sub req_Modified
     # Save the file data in $state
     $state->{entries}{$state->{directory}.$data}{modified_filename} = $filename;
     $state->{entries}{$state->{directory}.$data}{modified_mode} = $mode;
-    $state->{entries}{$state->{directory}.$data}{modified_hash} = `git-hash-object $filename`;
+    $state->{entries}{$state->{directory}.$data}{modified_hash} = `git hash-object $filename`;
     $state->{entries}{$state->{directory}.$data}{modified_hash} =~ s/\s.*$//s;
 
     #$log->debug("req_Modified : file=$data mode=$mode size=$size");
@@ -1289,7 +1289,7 @@ sub req_ci
 
        # do a checkout of the file if it is part of this tree
         if ($wrev) {
-            system('git-checkout-index', '-f', '-u', $filename);
+            system('git', 'checkout-index', '-f', '-u', $filename);
             unless ($? == 0) {
                 die "Error running git-checkout-index -f -u $filename : $!";
             }
@@ -1331,15 +1331,15 @@ sub req_ci
         {
             $log->info("Removing file '$filename'");
             unlink($filename);
-            system("git-update-index", "--remove", $filename);
+            system("git", "update-index", "--remove", $filename);
         }
         elsif ( $addflag )
         {
             $log->info("Adding file '$filename'");
-            system("git-update-index", "--add", $filename);
+            system("git", "update-index", "--add", $filename);
         } else {
             $log->info("Updating file '$filename'");
-            system("git-update-index", $filename);
+            system("git", "update-index", $filename);
         }
     }
 
@@ -1351,7 +1351,7 @@ sub req_ci
         return;
     }
 
-    my $treehash = `git-write-tree`;
+    my $treehash = `git write-tree`;
     chomp $treehash;
 
     $log->debug("Treehash : $treehash, Parenthash : $parenthash");
@@ -1368,7 +1368,7 @@ sub req_ci
     }
     close $msg_fh;
 
-    my $commithash = `git-commit-tree $treehash -p $parenthash < $msg_filename`;
+    my $commithash = `git commit-tree $treehash -p $parenthash < $msg_filename`;
     chomp($commithash);
     $log->info("Commit hash : $commithash");
 
@@ -1821,7 +1821,7 @@ sub req_annotate
        # TODO: if we got a revision from the client, use that instead
        # to look up the commithash in sqlite (still good to default to
        # the current head as we do now)
-       system("git-read-tree", $lastseenin);
+       system("git", "read-tree", $lastseenin);
        unless ($? == 0)
        {
            print "E error running git-read-tree $lastseenin $ENV{GIT_INDEX_FILE} $!\n";
@@ -1830,7 +1830,7 @@ sub req_annotate
        $log->info("Created index '$ENV{GIT_INDEX_FILE}' with commit $lastseenin - exit status $?");
 
         # do a checkout of the file
-        system('git-checkout-index', '-f', '-u', $filename);
+        system('git', 'checkout-index', '-f', '-u', $filename);
         unless ($? == 0) {
             print "E error running git-checkout-index -f -u $filename : $!\n";
             return;
@@ -1861,7 +1861,7 @@ sub req_annotate
         close ANNOTATEHINTS
             or (print "E failed to write $a_hints: $!\n"), return;
 
-        my @cmd = (qw(git-annotate -l -S), $a_hints, $filename);
+        my @cmd = (qw(git annotate -l -S), $a_hints, $filename);
         if (!open(ANNOTATE, "-|", @cmd)) {
             print "E error invoking ". join(' ',@cmd) .": $!\n";
             return;
@@ -2078,17 +2078,17 @@ sub transmitfile
 
     die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{40}$/ );
 
-    my $type = `git-cat-file -t $filehash`;
+    my $type = `git cat-file -t $filehash`;
     chomp $type;
 
     die ( "Invalid type '$type' (expected 'blob')" ) unless ( defined ( $type ) and $type eq "blob" );
 
-    my $size = `git-cat-file -s $filehash`;
+    my $size = `git cat-file -s $filehash`;
     chomp $size;
 
     $log->debug("transmitfile($filehash) size=$size, type=$type");
 
-    if ( open my $fh, '-|', "git-cat-file", "blob", $filehash )
+    if ( open my $fh, '-|', "git", "cat-file", "blob", $filehash )
     {
         if ( defined ( $options->{targetfile} ) )
         {
@@ -2935,7 +2935,7 @@ sub update
         push @git_log_params, $self->{module};
     }
     # git-rev-list is the backend / plumbing version of git-log
-    open(GITLOG, '-|', 'git-rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
+    open(GITLOG, '-|', 'git', 'rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
 
     my @commits;
 
@@ -3021,7 +3021,7 @@ sub update
                         next;
                     }
                    my $base = eval {
-                           safe_pipe_capture('git-merge-base',
+                           safe_pipe_capture('git', 'merge-base',
                                                 $lastpicked, $parent);
                    };
                    # The two branches may not be related at all,
@@ -3033,7 +3033,7 @@ sub update
                     if ($base) {
                         my @merged;
                         # print "want to log between  $base $parent \n";
-                        open(GITLOG, '-|', 'git-log', '--pretty=medium', "$base..$parent")
+                        open(GITLOG, '-|', 'git', 'log', '--pretty=medium', "$base..$parent")
                          or die "Cannot call git-log: $!";
                         my $mergedhash;
                         while (<GITLOG>) {
@@ -3075,7 +3075,7 @@ sub update
 
         if ( defined ( $lastpicked ) )
         {
-            my $filepipe = open(FILELIST, '-|', 'git-diff-tree', '-z', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
+            my $filepipe = open(FILELIST, '-|', 'git', 'diff-tree', '-z', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
            local ($/) = "\0";
             while ( <FILELIST> )
             {
@@ -3149,7 +3149,7 @@ sub update
             # this is used to detect files removed from the repo
             my $seen_files = {};
 
-            my $filepipe = open(FILELIST, '-|', 'git-ls-tree', '-z', '-r', $commit->{hash}) or die("Cannot call git-ls-tree : $!");
+            my $filepipe = open(FILELIST, '-|', 'git', 'ls-tree', '-z', '-r', $commit->{hash}) or die("Cannot call git-ls-tree : $!");
            local $/ = "\0";
             while ( <FILELIST> )
             {
@@ -3451,7 +3451,7 @@ sub commitmessage
         return $message;
     }
 
-    my @lines = safe_pipe_capture("git-cat-file", "commit", $commithash);
+    my @lines = safe_pipe_capture("git", "cat-file", "commit", $commithash);
     shift @lines while ( $lines[0] =~ /\S/ );
     $message = join("",@lines);
     $message .= " " if ( $message =~ /\n$/ );
index 37e044db40bb89470bb64a4108c86d5173e03b5a..a480d6fc70a4ca738c8ead14052afc3c1f5e8b02 100755 (executable)
@@ -97,12 +97,12 @@ set_ident () {
        echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
 }
 
-USAGE="[--env-filter <command>] [--tree-filter <command>] \
-[--index-filter <command>] [--parent-filter <command>] \
-[--msg-filter <command>] [--commit-filter <command>] \
-[--tag-name-filter <command>] [--subdirectory-filter <directory>] \
-[--original <namespace>] [-d <directory>] [-f | --force] \
-[<rev-list options>...]"
+USAGE="[--env-filter <command>] [--tree-filter <command>]
+            [--index-filter <command>] [--parent-filter <command>]
+            [--msg-filter <command>] [--commit-filter <command>]
+            [--tag-name-filter <command>] [--subdirectory-filter <directory>]
+            [--original <namespace>] [-d <directory>] [-f | --force]
+            [<rev-list options>...]"
 
 OPTIONS_SPEC=
 . git-sh-setup
index 5f4419b69b58769e8fee7a60109520a095a831c5..622a5f0eb25e3420c085d51fd85944ab1286147e 100755 (executable)
@@ -77,11 +77,30 @@ start_httpd () {
        resolve_full_httpd
 
        # don't quote $full_httpd, there can be arguments to it (-f)
-       $full_httpd "$fqgitdir/gitweb/httpd.conf"
-       if test $? != 0; then
-               echo "Could not execute http daemon $httpd."
-               exit 1
-       fi
+       case "$httpd" in
+       *mongoose*)
+               #The mongoose server doesn't have a daemon mode so we'll have to fork it
+               $full_httpd "$fqgitdir/gitweb/httpd.conf" &
+               #Save the pid before doing anything else (we'll print it later)
+               pid=$!
+
+               if test $? != 0; then
+                       echo "Could not execute http daemon $httpd."
+                       exit 1
+               fi
+
+               cat > "$fqgitdir/pid" <<EOF
+$pid
+EOF
+               ;;
+       *)
+               $full_httpd "$fqgitdir/gitweb/httpd.conf"
+               if test $? != 0; then
+                       echo "Could not execute http daemon $httpd."
+                       exit 1
+               fi
+               ;;
+       esac
 }
 
 stop_httpd () {
@@ -256,7 +275,7 @@ apache2_conf () {
        mkdir -p "$GIT_DIR/gitweb/logs"
        bind=
        test x"$local" = xtrue && bind='127.0.0.1:'
-       echo 'text/css css' > $fqgitdir/mime.types
+       echo 'text/css css' > "$fqgitdir/mime.types"
        cat > "$conf" <<EOF
 ServerName "git-instaweb"
 ServerRoot "$fqgitdir/gitweb"
@@ -272,13 +291,13 @@ EOF
                fi
        done
        cat >> "$conf" <<EOF
-TypesConfig $fqgitdir/mime.types
+TypesConfig "$fqgitdir/mime.types"
 DirectoryIndex gitweb.cgi
 EOF
 
        # check to see if Dennis Stosberg's mod_perl compatibility patch
        # (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
-       if test -f "$module_path/mod_perl.so" && grep '^our $gitbin' \
+       if test -f "$module_path/mod_perl.so" && grep 'MOD_PERL' \
                                "$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
        then
                # favor mod_perl if available
@@ -298,7 +317,21 @@ EOF
                resolve_full_httpd
                list_mods=$(echo "$full_httpd" | sed "s/-f$/-l/")
                $list_mods | grep 'mod_cgi\.c' >/dev/null 2>&1 || \
-               echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
+               if test -f "$module_path/mod_cgi.so"
+               then
+                       echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
+               else
+                       $list_mods | grep 'mod_cgid\.c' >/dev/null 2>&1 || \
+                       if test -f "$module_path/mod_cgid.so"
+                       then
+                               echo "LoadModule cgid_module $module_path/mod_cgid.so" \
+                                       >> "$conf"
+                       else
+                               echo "You have no CGI support!"
+                               exit 2
+                       fi
+                       echo "ScriptSock logs/gitweb.sock" >> "$conf"
+               fi
                cat >> "$conf" <<EOF
 AddHandler cgi-script .cgi
 <Location /gitweb.cgi>
@@ -308,6 +341,31 @@ EOF
        fi
 }
 
+mongoose_conf() {
+       cat > "$conf" <<EOF
+# Mongoose web server configuration file.
+# Lines starting with '#' and empty lines are ignored.
+# For detailed description of every option, visit
+# http://code.google.com/p/mongoose/wiki/MongooseManual
+
+root           $fqgitdir/gitweb
+ports          $port
+index_files    gitweb.cgi
+#ssl_cert      $fqgitdir/gitweb/ssl_cert.pem
+error_log      $fqgitdir/gitweb/error.log
+access_log     $fqgitdir/gitweb/access.log
+
+#cgi setup
+cgi_env                PATH=/usr/local/bin:/usr/bin:/bin,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
+cgi_interp     $PERL
+cgi_ext                cgi,pl
+
+# mimetype mapping
+mime_types     .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-tgz,.tar=application/x-tar,.zip=application/zip,.gif=image/gif,.jpg=image/jpeg,.jpeg=image/jpeg,.png=image/png,.css=text/css,.html=text/html,.htm=text/html,.js=text/javascript,.c=text/plain,.cpp=text/plain,.log=text/plain,.conf=text/plain,.text=text/plain,.txt=text/plain,.dtd=text/xml,.bz2=application/x-bzip,.tbz=application/x-bzip-compressed-tar,.tar.bz2=application/x-bzip-compressed-tar
+EOF
+}
+
+
 script='
 s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#;
 s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#;
@@ -344,6 +402,9 @@ case "$httpd" in
 webrick)
        webrick_conf
        ;;
+*mongoose*)
+       mongoose_conf
+       ;;
 *)
        echo "Unknown httpd specified: $httpd"
        exit 1
index e1eb9632660146396a0b5f3f2a410d8cb027ff9d..9c2c1b7202462370509a7bdb391982ae32564bd6 100755 (executable)
@@ -113,6 +113,10 @@ case "${1:-.}${2:-.}${3:-.}" in
        src1=`git-unpack-file $2`
        git merge-file "$src1" "$orig" "$src2"
        ret=$?
+       msg=
+       if [ $ret -ne 0 ]; then
+               msg='content conflict'
+       fi
 
        # Create the working tree file, using "our tree" version from the
        # index, and then store the result of the merge.
@@ -120,7 +124,10 @@ case "${1:-.}${2:-.}${3:-.}" in
        rm -f -- "$orig" "$src1" "$src2"
 
        if [ "$6" != "$7" ]; then
-               echo "ERROR: Permissions conflict: $5->$6,$7."
+               if [ -n "$msg" ]; then
+                       msg="$msg, "
+               fi
+               msg="${msg}permissions conflict: $5->$6,$7"
                ret=1
        fi
        if [ "$1" = '' ]; then
@@ -128,7 +135,7 @@ case "${1:-.}${2:-.}${3:-.}" in
        fi
 
        if [ $ret -ne 0 ]; then
-               echo "ERROR: Merge conflict in $4"
+               echo "ERROR: $msg in $4"
                exit 1
        fi
        exec git update-index -- "$4"
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..fc78592ae04283db2af633ec080f99c6404ceda7 100755 (executable)
@@ -89,14 +89,24 @@ error_on_no_merge_candidates () {
        done
 
        curr_branch=${curr_branch#refs/heads/}
-
-       if [ -z "$curr_branch" ]; then
+       upstream=$(git config "branch.$curr_branch.merge")
+       remote=$(git config "branch.$curr_branch.remote")
+
+       if [ $# -gt 1 ]; then
+               echo "There are no candidates for merging in the refs that you just fetched."
+               echo "Generally this means that you provided a wildcard refspec which had no"
+               echo "matches on the remote end."
+       elif [ $# -gt 0 ] && [ "$1" != "$remote" ]; then
+               echo "You asked to pull from the remote '$1', but did not specify"
+               echo "a branch to merge. Because this is not the default configured remote"
+               echo "for your current branch, you must specify a branch on the command line."
+       elif [ -z "$curr_branch" ]; then
                echo "You are not currently on a branch, so I cannot use any"
                echo "'branch.<branchname>.merge' in your configuration file."
                echo "Please specify which branch you want to merge on the command"
                echo "line and try again (e.g. 'git pull <repository> <refspec>')."
                echo "See git-pull(1) for details."
-       else
+       elif [ -z "$upstream" ]; then
                echo "You asked me to pull without telling me which branch you"
                echo "want to merge with, and 'branch.${curr_branch}.merge' in"
                echo "your configuration file does not tell me either.  Please"
@@ -114,23 +124,39 @@ error_on_no_merge_candidates () {
                echo "    remote.<nickname>.fetch = <refspec>"
                echo
                echo "See git-config(1) for details."
+       else
+               echo "Your configuration specifies to merge the ref '${upstream#refs/heads/}' from the"
+               echo "remote, but no such ref was fetched."
        fi
        exit 1
 }
 
 test true = "$rebase" && {
-       git update-index --ignore-submodules --refresh &&
-       git diff-files --ignore-submodules --quiet &&
-       git diff-index --ignore-submodules --cached --quiet HEAD -- ||
-       die "refusing to pull with rebase: your working tree is not up-to-date"
-
+       if ! git rev-parse -q --verify HEAD >/dev/null
+       then
+               # On an unborn branch
+               if test -f "$GIT_DIR/index"
+               then
+                       die "updating an unborn branch with changes added to the index"
+               fi
+       else
+               git update-index --ignore-submodules --refresh &&
+               git diff-files --ignore-submodules --quiet &&
+               git diff-index --ignore-submodules --cached --quiet HEAD -- ||
+               die "refusing to pull with rebase: your working tree is not up-to-date"
+       fi
+       oldremoteref= &&
        . 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|")" &&
-       oldremoteref="$(git rev-parse -q --verify \
-               "refs/remotes/$origin/$reflist")"
+       remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
+       oldremoteref="$(git rev-parse -q --verify "$remoteref")" &&
+       for reflog in $(git rev-list -g $remoteref 2>/dev/null)
+       do
+               if test "$reflog" = "$(git merge-base $reflog $curr_branch)"
+               then
+                       oldremoteref="$reflog"
+                       break
+               fi
+       done
 }
 orig_head=$(git rev-parse -q --verify HEAD)
 git fetch $verbosity --update-head-ok "$@" || exit 1
@@ -164,25 +190,16 @@ merge_head=$(sed -e '/    not-for-merge   /d' \
 
 case "$merge_head" in
 '')
-       case $? in
-       0) error_on_no_merge_candidates "$@";;
-       1) echo >&2 "You are not currently on a branch; you must explicitly"
-          echo >&2 "specify which branch you wish to merge:"
-          echo >&2 "  git pull <remote> <branch>"
-          exit 1;;
-       *) exit $?;;
-       esac
+       error_on_no_merge_candidates "$@"
        ;;
 ?*' '?*)
        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..23ded48322bf795b06aa40e4684bc710c8232549 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
                ;;
@@ -703,7 +703,7 @@ first and then run 'git rebase --continue' again."
                                        preserve=t
                                        for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
                                        do
-                                               if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \)
+                                               if test -f "$REWRITTEN"/$p -a \( $p != $ONTO -o $sha1 = $first_after_upstream \)
                                                then
                                                        preserve=f
                                                fi
index b83fd3f970161d9ca8a4212a0b335f784fc3ff84..6ec155cf03c98f2e075f298039037d0e099311b1 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano.
 #
 
-USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>]'
+USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
 same name.  When the --onto option is provided the new branch starts
 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -72,11 +72,20 @@ continue_merge () {
                        echo "directly, but instead do one of the following: "
                        die "$RESOLVEMSG"
                fi
-               printf "Committed: %0${prec}d " $msgnum
+               if test -z "$GIT_QUIET"
+               then
+                       printf "Committed: %0${prec}d " $msgnum
+               fi
        else
-               printf "Already applied: %0${prec}d " $msgnum
+               if test -z "$GIT_QUIET"
+               then
+                       printf "Already applied: %0${prec}d " $msgnum
+               fi
+       fi
+       if test -z "$GIT_QUIET"
+       then
+               git rev-list --pretty=oneline -1 "$cmt" | sed -e 's/^[^ ]* //'
        fi
-       git rev-list --pretty=oneline -1 "$cmt" | sed -e 's/^[^ ]* //'
 
        prev_head=`git rev-parse HEAD^0`
        # save the resulting commit so we can read-tree on it later
@@ -97,6 +106,10 @@ call_merge () {
        eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
        eval GITHEAD_$hd='$(cat "$dotest/onto_name")'
        export GITHEAD_$cmt GITHEAD_$hd
+       if test -n "$GIT_QUIET"
+       then
+               export GIT_MERGE_VERBOSITY=1
+       fi
        git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
        rv=$?
        case "$rv" in
@@ -138,7 +151,7 @@ move_to_original_branch () {
 finish_rb_merge () {
        move_to_original_branch
        rm -r "$dotest"
-       echo "All done."
+       say All done.
 }
 
 is_interactive () {
@@ -168,10 +181,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
 }
 
@@ -209,6 +220,7 @@ do
                        end=$(cat "$dotest/end")
                        msgnum=$(cat "$dotest/msgnum")
                        onto=$(cat "$dotest/onto")
+                       GIT_QUIET=$(cat "$dotest/quiet")
                        continue_merge
                        while test "$msgnum" -le "$end"
                        do
@@ -221,6 +233,7 @@ do
                head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
                onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
                orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
+               GIT_QUIET=$(cat "$GIT_DIR"/rebase-apply/quiet)
                git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
                move_to_original_branch
                exit
@@ -238,6 +251,7 @@ do
                        msgnum=$(cat "$dotest/msgnum")
                        msgnum=$(($msgnum + 1))
                        onto=$(cat "$dotest/onto")
+                       GIT_QUIET=$(cat "$dotest/quiet")
                        while test "$msgnum" -le "$end"
                        do
                                call_merge "$msgnum"
@@ -249,6 +263,7 @@ do
                head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
                onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
                orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
+               GIT_QUIET=$(cat "$GIT_DIR"/rebase-apply/quiet)
                git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
                move_to_original_branch
                exit
@@ -260,9 +275,11 @@ do
                git rerere clear
                if test -d "$dotest"
                then
+                       GIT_QUIET=$(cat "$dotest/quiet")
                        move_to_original_branch
                else
                        dotest="$GIT_DIR"/rebase-apply
+                       GIT_QUIET=$(cat "$dotest/quiet")
                        move_to_original_branch
                fi
                git reset --hard $(cat "$dotest/orig-head")
@@ -300,6 +317,13 @@ do
        -v|--verbose)
                verbose=t
                diffstat=t
+               GIT_QUIET=
+               ;;
+       -q|--quiet)
+               GIT_QUIET=t
+               git_am_opt="$git_am_opt -q"
+               verbose=
+               diffstat=
                ;;
        --whitespace=*)
                git_am_opt="$git_am_opt $1"
@@ -309,6 +333,9 @@ do
                        ;;
                esac
                ;;
+       --ignore-whitespace)
+               git_am_opt="$git_am_opt $1"
+               ;;
        --committer-date-is-author-date|--ignore-date)
                git_am_opt="$git_am_opt $1"
                force_rebase=t
@@ -358,8 +385,9 @@ else
 fi
 
 # The tree must be really really clean.
-if ! git update-index --ignore-submodules --refresh; then
+if ! git update-index --ignore-submodules --refresh > /dev/null; then
        echo >&2 "cannot rebase: you have unstaged changes"
+       git diff-files --name-status -r --ignore-submodules -- >&2
        exit 1
 fi
 diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
@@ -445,15 +473,15 @@ then
        then
                # Lazily switch to the target branch if needed...
                test -z "$switch_to" || git checkout "$switch_to"
-               echo >&2 "Current branch $branch_name is up to date."
+               say "Current branch $branch_name is up to date."
                exit 0
        else
-               echo "Current branch $branch_name is up to date, rebase forced."
+               say "Current branch $branch_name is up to date, rebase forced."
        fi
 fi
 
 # Detach HEAD and reset the tree
-echo "First, rewinding head to replay your work on top of it..."
+say "First, rewinding head to replay your work on top of it..."
 git checkout -q "$onto^0" || die "could not detach HEAD"
 git update-ref ORIG_HEAD $branch
 
@@ -471,7 +499,7 @@ fi
 # we just fast forwarded.
 if test "$mb" = "$branch"
 then
-       echo >&2 "Fast-forwarded $branch_name to $onto_name."
+       say "Fast-forwarded $branch_name to $onto_name."
        move_to_original_branch
        exit 0
 fi
@@ -493,7 +521,8 @@ then
        test 0 != $ret -a -d "$GIT_DIR"/rebase-apply &&
                echo $head_name > "$GIT_DIR"/rebase-apply/head-name &&
                echo $onto > "$GIT_DIR"/rebase-apply/onto &&
-               echo $orig_head > "$GIT_DIR"/rebase-apply/orig-head
+               echo $orig_head > "$GIT_DIR"/rebase-apply/orig-head &&
+               echo "$GIT_QUIET" > "$GIT_DIR"/rebase-apply/quiet
        exit $ret
 fi
 
@@ -507,6 +536,7 @@ prev_head=$orig_head
 echo "$prev_head" > "$dotest/prev_head"
 echo "$orig_head" > "$dotest/orig-head"
 echo "$head_name" > "$dotest/head-name"
+echo "$GIT_QUIET" > "$dotest/quiet"
 
 msgnum=0
 for cmt in `git rev-list --reverse --no-merges "$revisions"`
index 0868734723b3c96144bfa9360a9e19ebae1995f7..1eb3bca352f38ec3807383cc5fff7f7e62731b2b 100755 (executable)
@@ -24,7 +24,7 @@ SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
 no_update_info= all_into_one= remove_redundant= unpack_unreachable=
-local= quiet= no_reuse= extra=
+local= no_reuse= extra=
 while test $# != 0
 do
        case "$1" in
@@ -33,7 +33,7 @@ do
        -A)     all_into_one=t
                unpack_unreachable=--unpack-unreachable ;;
        -d)     remove_redundant=t ;;
-       -q)     quiet=-q ;;
+       -q)     GIT_QUIET=t ;;
        -f)     no_reuse=--no-reuse-object ;;
        -l)     local=--local ;;
        --max-pack-size|--window|--window-memory|--depth)
@@ -80,13 +80,11 @@ case ",$all_into_one," in
        ;;
 esac
 
-args="$args $local $quiet $no_reuse$extra"
-names=$(git pack-objects --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
+args="$args $local ${GIT_QUIET:+-q} $no_reuse$extra"
+names=$(git pack-objects --keep-true-parents --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
        exit 1
 if [ -z "$names" ]; then
-       if test -z "$quiet"; then
-               echo Nothing new to pack.
-       fi
+       say Nothing new to pack.
 fi
 
 # Ok we have prepared all new packfiles.
@@ -176,7 +174,7 @@ then
                  done
                )
        fi
-       git prune-packed $quiet
+       git prune-packed ${GIT_QUIET:+-q}
 fi
 
 case "$no_update_info" in
index a2cf5b82150a77fd9ddb775fea1bc8d5e8ee7392..630ceddf0356429f7ff71d280ee1056a2eb939c6 100755 (executable)
@@ -8,10 +8,33 @@ USAGE='<start> <url> [<end>]'
 LONG_USAGE='Summarizes the changes between two commits to the standard output,
 and includes the given URL in the generated summary.'
 SUBDIRECTORY_OK='Yes'
-OPTIONS_SPEC=
+OPTIONS_SPEC='git request-pull [options] start url [end]
+--
+p    show patch text as well
+'
+
 . git-sh-setup
 . git-parse-remote
 
+GIT_PAGER=
+export GIT_PAGER
+
+patch=
+while  case "$#" in 0) break ;; esac
+do
+       case "$1" in
+       -p)
+               patch=-p ;;
+       --)
+               shift; break ;;
+       -*)
+               usage ;;
+       *)
+               break ;;
+       esac
+       shift
+done
+
 base=$1
 url=$2
 head=${3-HEAD}
@@ -25,16 +48,16 @@ headrev=`git rev-parse --verify "$head"^0` || exit
 merge_base=`git merge-base $baserev $headrev` ||
 die "fatal: No commits in common between $base and $head"
 
-url=$(get_remote_url "$url")
 branch=$(git ls-remote "$url" \
        | sed -n -e "/^$headrev refs.heads./{
                s/^.*   refs.heads.//
                p
                q
        }")
+url=$(get_remote_url "$url")
 if [ -z "$branch" ]; then
        echo "warn: No branch of $url is at:" >&2
-       git log --max-count=1 --pretty='format:warn:   %h: %s' $headrev >&2
+       git log --max-count=1 --pretty='tformat:warn:   %h: %s' $headrev >&2
        echo "warn: Are you sure you pushed $head there?" >&2
        echo >&2
        echo >&2
@@ -42,8 +65,6 @@ if [ -z "$branch" ]; then
        status=1
 fi
 
-PAGER=
-export PAGER
 echo "The following changes since commit $baserev:"
 git shortlog --max-count=1 $baserev | sed -e 's/^\(.\)/  \1/'
 
@@ -53,5 +74,5 @@ echo "  $url $branch"
 echo
 
 git shortlog ^$baserev $headrev
-git diff -M --stat --summary $merge_base $headrev
+git diff -M --stat --summary $patch $merge_base..$headrev
 exit $status
index cccbf4517aa46d4d217a1ce25105f7c413301d2d..f5ba4e7699936499021a8a955b198668b98a1696 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'};
@@ -400,7 +401,7 @@ my %aliases;
 my %parse_alias = (
        # multiline formats can be supported in the future
        mutt => sub { my $fh = shift; while (<$fh>) {
-               if (/^\s*alias\s+(\S+)\s+(.*)$/) {
+               if (/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) {
                        my ($alias, $addr) = ($1, $2);
                        $addr =~ s/#.*$//; # mutt allows # comments
                         # commas delimit multiple addresses
@@ -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 = $_) {
@@ -449,7 +450,6 @@ sub check_file_rev_conflict($) {
        try {
                $repo->command('rev-parse', '--verify', '--quiet', $f);
                if (defined($format_patch)) {
-                       print "foo\n";
                        return $format_patch;
                }
                die(<<EOF);
@@ -537,7 +537,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 +552,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 +568,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 +576,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) {
@@ -655,13 +653,17 @@ if (!@to) {
 }
 
 sub expand_aliases {
-       my @cur = @_;
-       my @last;
-       do {
-               @last = @cur;
-               @cur = map { $aliases{$_} ? @{$aliases{$_}} : $_ } @last;
-       } while (join(',',@cur) ne join(',',@last));
-       return @cur;
+       return map { expand_one_alias($_) } @_;
+}
+
+my %EXPANDED_ALIASES;
+sub expand_one_alias {
+       my $alias = shift;
+       if ($EXPANDED_ALIASES{$alias}) {
+               die "fatal: alias '$alias' expands to itself\n";
+       }
+       local $EXPANDED_ALIASES{$alias} = 1;
+       return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
 }
 
 @to = expand_aliases(@to);
@@ -767,12 +769,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 +794,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 +814,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 +886,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);
@@ -907,7 +921,7 @@ X-Mailer: git-send-email $gitversion
                        $smtp ||= Net::SMTP->new((defined $smtp_server_port)
                                                 ? "$smtp_server:$smtp_server_port"
                                                 : $smtp_server);
-                       if ($smtp_encryption eq 'tls') {
+                       if ($smtp_encryption eq 'tls' && $smtp) {
                                require Net::SMTP::SSL;
                                $smtp->command('STARTTLS');
                                $smtp->response();
@@ -953,7 +967,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 +988,8 @@ X-Mailer: git-send-email $gitversion
                        print "Result: OK\n";
                }
        }
+
+       return 1;
 }
 
 $reply_to = $initial_reply_to;
@@ -1091,7 +1107,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 +1150,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..c41c2f7439724adc3dd13b92c86b25d254fc23b8 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
 }
 
+GIT_QUIET=
+
+say () {
+       if test -z "$GIT_QUIET"
+       then
+               printf '%s\n' "$*"
+       fi
+}
+
 if test -n "$OPTIONS_SPEC"; then
        usage() {
                "$0" -h
index b9ace9970492aaf48472904d978d809d90ca33db..4febbbfa5d13b241e9bfcc479c6767a90bd12919 100755 (executable)
@@ -3,10 +3,11 @@
 
 dashless=$(basename "$0" | sed -e 's/-/ /')
 USAGE="list [<options>]
-   or: $dashless (show | drop | pop ) [<stash>]
-   or: $dashless apply [--index] [<stash>]
+   or: $dashless show [<stash>]
+   or: $dashless drop [-q|--quiet] [<stash>]
+   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
    or: $dashless branch <branchname> [<stash>]
-   or: $dashless [save [--keep-index] [<message>]]
+   or: $dashless [save [-k|--keep-index] [-q|--quiet] [<message>]]
    or: $dashless clear"
 
 SUBDIRECTORY_OK=Yes
@@ -20,6 +21,14 @@ trap 'rm -f "$TMP-*"' 0
 
 ref_stash=refs/stash
 
+if git config --get-colorbool color.interactive; then
+       help_color="$(git config --get-color color.interactive.help 'red bold')"
+       reset_color="$(git config --get-color '' reset)"
+else
+       help_color=
+       reset_color=
+fi
+
 no_changes () {
        git diff-index --quiet --cached HEAD --ignore-submodules -- &&
        git diff-files --quiet --ignore-submodules
@@ -67,19 +76,44 @@ create_stash () {
                git commit-tree $i_tree -p $b_commit) ||
                die "Cannot save the current index state"
 
-       # state of the working tree
-       w_tree=$( (
+       if test -z "$patch_mode"
+       then
+
+               # state of the working tree
+               w_tree=$( (
+                       rm -f "$TMP-index" &&
+                       cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
+                       GIT_INDEX_FILE="$TMP-index" &&
+                       export GIT_INDEX_FILE &&
+                       git read-tree -m $i_tree &&
+                       git add -u &&
+                       git write-tree &&
+                       rm -f "$TMP-index"
+               ) ) ||
+                       die "Cannot save the current worktree state"
+
+       else
+
                rm -f "$TMP-index" &&
-               cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
-               GIT_INDEX_FILE="$TMP-index" &&
-               export GIT_INDEX_FILE &&
-               git read-tree -m $i_tree &&
-               git add -u &&
-               git write-tree &&
-               rm -f "$TMP-index"
-       ) ||
+               GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
+
+               # find out what the user wants
+               GIT_INDEX_FILE="$TMP-index" \
+                       git add--interactive --patch=stash -- &&
+
+               # state of the working tree
+               w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
                die "Cannot save the current worktree state"
 
+               git diff-tree -p HEAD $w_tree > "$TMP-patch" &&
+               test -s "$TMP-patch" ||
+               die "No changes selected"
+
+               rm -f "$TMP-index" ||
+               die "Cannot remove temporary index (can't happen)"
+
+       fi
+
        # create the stash
        if test -z "$stash_msg"
        then
@@ -94,18 +128,44 @@ create_stash () {
 
 save_stash () {
        keep_index=
-       case "$1" in
-       --keep-index)
-               keep_index=t
+       patch_mode=
+       while test $# != 0
+       do
+               case "$1" in
+               -k|--keep-index)
+                       keep_index=t
+                       ;;
+               --no-keep-index)
+                       keep_index=
+                       ;;
+               -p|--patch)
+                       patch_mode=t
+                       keep_index=t
+                       ;;
+               -q|--quiet)
+                       GIT_QUIET=t
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       echo "error: unknown option for 'stash save': $1"
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
                shift
-       esac
+       done
 
        stash_msg="$*"
 
        git update-index -q --refresh
        if no_changes
        then
-               echo 'No local changes to save'
+               say 'No local changes to save'
                exit 0
        fi
        test -f "$GIT_DIR/logs/$ref_stash" ||
@@ -118,13 +178,24 @@ save_stash () {
 
        git update-ref -m "$stash_msg" $ref_stash $w_commit ||
                die "Cannot save the current status"
-       printf 'Saved working directory and index state "%s"\n' "$stash_msg"
-
-       git reset --hard
+       say Saved working directory and index state "$stash_msg"
 
-       if test -n "$keep_index" && test -n $i_tree
+       if test -z "$patch_mode"
        then
-               git read-tree --reset -u $i_tree
+               git reset --hard ${GIT_QUIET:+-q}
+
+               if test -n "$keep_index" && test -n $i_tree
+               then
+                       git read-tree --reset -u $i_tree
+               fi
+       else
+               git apply -R < "$TMP-patch" ||
+               die "Cannot remove worktree changes"
+
+               if test -z "$keep_index"
+               then
+                       git reset
+               fi
        fi
 }
 
@@ -151,29 +222,45 @@ show_stash () {
 }
 
 apply_stash () {
-       git update-index -q --refresh &&
-       git diff-files --quiet --ignore-submodules ||
-               die 'Cannot apply to a dirty working tree, please stage your changes'
-
        unstash_index=
-       case "$1" in
-       --index)
-               unstash_index=t
+
+       while test $# != 0
+       do
+               case "$1" in
+               --index)
+                       unstash_index=t
+                       ;;
+               -q|--quiet)
+                       GIT_QUIET=t
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
                shift
-       esac
+       done
 
-       # current index state
-       c_tree=$(git write-tree) ||
-               die 'Cannot apply a stash in the middle of a merge'
+       if test $# = 0
+       then
+               have_stash || die 'Nothing to apply'
+       fi
 
        # stash records the work tree, and is a merge between the
        # base commit (first parent) and the index tree (second parent).
-       s=$(git rev-parse --verify --default $ref_stash "$@") &&
-       w_tree=$(git rev-parse --verify "$s:") &&
-       b_tree=$(git rev-parse --verify "$s^1:") &&
-       i_tree=$(git rev-parse --verify "$s^2:") ||
+       s=$(git rev-parse --quiet --verify --default $ref_stash "$@") &&
+       w_tree=$(git rev-parse --quiet --verify "$s:") &&
+       b_tree=$(git rev-parse --quiet --verify "$s^1:") &&
+       i_tree=$(git rev-parse --quiet --verify "$s^2:") ||
                die "$*: no valid stashed state found"
 
+       git update-index -q --refresh &&
+       git diff-files --quiet --ignore-submodules ||
+               die 'Cannot apply to a dirty working tree, please stage your changes'
+
+       # current index state
+       c_tree=$(git write-tree) ||
+               die 'Cannot apply a stash in the middle of a merge'
+
        unstashed_index_tree=
        if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
                        test "$c_tree" != "$i_tree"
@@ -181,7 +268,7 @@ apply_stash () {
                git diff-tree --binary $s^2^..$s^2 | git apply --cached
                test $? -ne 0 &&
                        die 'Conflicts in index. Try without --index.'
-               unstashed_index_tree=$(git-write-tree) ||
+               unstashed_index_tree=$(git write-tree) ||
                        die 'Could not save index tree'
                git reset
        fi
@@ -193,7 +280,11 @@ apply_stash () {
                export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
        "
 
-       if git-merge-recursive $b_tree -- $c_tree $w_tree
+       if test -n "$GIT_QUIET"
+       then
+               export GIT_MERGE_VERBOSITY=0
+       fi
+       if git merge-recursive $b_tree -- $c_tree $w_tree
        then
                # No conflict
                if test -n "$unstashed_index_tree"
@@ -207,7 +298,12 @@ apply_stash () {
                                die "Cannot unstage modified files"
                        rm -f "$a"
                fi
-               git status || :
+               squelch=
+               if test -n "$GIT_QUIET"
+               then
+                       squelch='>/dev/null 2>&1'
+               fi
+               eval "git status $squelch" || :
        else
                # Merge conflict; keep the exit status from merge-recursive
                status=$?
@@ -222,6 +318,19 @@ apply_stash () {
 drop_stash () {
        have_stash || die 'No stash entries to drop'
 
+       while test $# != 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       GIT_QUIET=t
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
        if test $# = 0
        then
                set x "$ref_stash@{0}"
@@ -235,7 +344,7 @@ drop_stash () {
                die "$*: not a valid stashed state"
 
        git reflog delete --updateref --rewrite "$@" &&
-               echo "Dropped $* ($s)" || die "$*: Could not drop stash entry"
+               say "Dropped $* ($s)" || die "$*: Could not drop stash entry"
 
        # clear_stash if we just dropped the last stash entry
        git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
@@ -253,11 +362,23 @@ apply_to_branch () {
        fi
        stash=$2
 
-       git-checkout -b $branch $stash^ &&
+       git checkout -b $branch $stash^ &&
        apply_stash --index $stash &&
        drop_stash $stash
 }
 
+# The default command is "save" if nothing but options are given
+seen_non_option=
+for opt
+do
+       case "$opt" in
+       -*) ;;
+       *) seen_non_option=t; break ;;
+       esac
+done
+
+test -n "$seen_non_option" || set "save" "$@"
+
 # Main command set
 case "$1" in
 list)
@@ -309,12 +430,13 @@ branch)
        apply_to_branch "$@"
        ;;
 *)
-       if test $# -eq 0
-       then
+       case $# in
+       0)
                save_stash &&
-               echo '(To restore them type "git stash apply")'
-       else
+               say '(To restore them type "git stash apply")'
+               ;;
+       *)
                usage
-       fi
+       esac
        ;;
 esac
index 8e234a4028d22e11baedba11f871d33f56945716..0462e529d934750fb63a5a897f135d60ac674b5e 100755 (executable)
@@ -4,9 +4,14 @@
 #
 # Copyright (c) 2007 Lars Hjemli
 
-USAGE="[--quiet] [--cached] \
-[add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch]|summary [-n|--summary-limit <n>] [<commit>]] \
-[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path>
+   or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] init [--] [<path>...]
+   or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+   or: $dashless [--quiet] foreach [--recursive] <command>
+   or: $dashless [--quiet] sync [--] [<path>...]"
 OPTIONS_SPEC=
 . git-sh-setup
 . git-parse-remote
@@ -14,20 +19,12 @@ require_work_tree
 
 command=
 branch=
-quiet=
+reference=
 cached=
+files=
 nofetch=
-
-#
-# print stuff on stdout unless -q was specified
-#
-say()
-{
-       if test -z "$quiet"
-       then
-               echo "$@"
-       fi
-}
+update=
+prefix=
 
 # Resolve relative url by appending to parent's url
 resolve_relative_url ()
@@ -91,6 +88,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
@@ -100,13 +98,18 @@ module_clone()
        if test -d "$path"
        then
                rmdir "$path" 2>/dev/null ||
-               die "Directory '$path' exist, but is neither empty nor a git repository"
+               die "Directory '$path' exists, but is neither empty nor a git repository"
        fi
 
        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"
 }
 
@@ -129,7 +132,16 @@ cmd_add()
                        shift
                        ;;
                -q|--quiet)
-                       quiet=1
+                       GIT_QUIET=1
+                       ;;
+               --reference)
+                       case "$2" in '') usage ;; esac
+                       reference="--reference=$2"
+                       shift
+                       ;;
+               --reference=*)
+                       reference="$1"
+                       shift
                        ;;
                --)
                        shift
@@ -203,7 +215,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" &&
@@ -232,13 +244,43 @@ cmd_add()
 #
 cmd_foreach()
 {
+       # parse $args after "submodule ... foreach".
+       while test $# -ne 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       GIT_QUIET=1
+                       ;;
+               --recursive)
+                       recursive=1
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
        module_list |
        while read mode sha1 stage path
        do
                if test -e "$path"/.git
                then
-                       say "Entering '$path'"
-                       (cd "$path" && eval "$@") ||
+                       say "Entering '$prefix$path'"
+                       name=$(module_name "$path")
+                       (
+                               prefix="$prefix$path/"
+                               unset GIT_DIR
+                               cd "$path" &&
+                               eval "$@" &&
+                               if test -n "$recursive"
+                               then
+                                       cmd_foreach "--recursive" "$@"
+                               fi
+                       ) ||
                        die "Stopping at '$path'; script returned non-zero status."
                fi
        done
@@ -256,7 +298,7 @@ cmd_init()
        do
                case "$1" in
                -q|--quiet)
-                       quiet=1
+                       GIT_QUIET=1
                        ;;
                --)
                        shift
@@ -294,6 +336,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
 }
@@ -306,21 +353,43 @@ cmd_init()
 cmd_update()
 {
        # parse $args after "submodule ... update".
+       orig_args="$@"
        while test $# -ne 0
        do
                case "$1" in
                -q|--quiet)
                        shift
-                       quiet=1
+                       GIT_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
+                       ;;
+               -m|--merge)
+                       shift
+                       update="merge"
+                       ;;
+               --recursive)
+                       shift
+                       recursive=1
+                       ;;
                --)
                        shift
                        break
@@ -334,11 +403,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 +426,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 +434,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 +454,33 @@ 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"
+                               ;;
+                       merge)
+                               command="git merge"
+                               action="merge"
+                               msg="merged in"
+                               ;;
+                       *)
+                               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
+
+               if test -n "$recursive"
+               then
+                       (unset GIT_DIR; cd "$path" && cmd_update $orig_args) ||
+                       die "Failed to recurse into submodule path '$path'"
                fi
        done
 }
@@ -406,6 +508,7 @@ set_name_rev () {
 cmd_summary() {
        summary_limit=-1
        for_status=
+       diff_cmd=diff-index
 
        # parse $args after "submodule ... summary".
        while test $# -ne 0
@@ -414,6 +517,9 @@ cmd_summary() {
                --cached)
                        cached="$1"
                        ;;
+               --files)
+                       files="$1"
+                       ;;
                --for-status)
                        for_status="$1"
                        ;;
@@ -450,9 +556,17 @@ cmd_summary() {
                head=HEAD
        fi
 
+       if [ -n "$files" ]
+       then
+               test -n "$cached" &&
+               die "--cached cannot be used with --files"
+               diff_cmd=diff-files
+               head=
+       fi
+
        cd_to_toplevel
        # Get modified modules cared by user
-       modules=$(git diff-index $cached --raw $head -- "$@" |
+       modules=$(git $diff_cmd $cached --raw $head -- "$@" |
                egrep '^:([0-7]* )?160000' |
                while read mod_src mod_dst sha1_src sha1_dst status name
                do
@@ -466,7 +580,7 @@ cmd_summary() {
 
        test -z "$modules" && return
 
-       git diff-index $cached --raw $head -- $modules |
+       git $diff_cmd $cached --raw $head -- $modules |
        egrep '^:([0-7]* )?160000' |
        cut -c2- |
        while read mod_src mod_dst sha1_src sha1_dst status name
@@ -589,15 +703,19 @@ cmd_summary() {
 cmd_status()
 {
        # parse $args after "submodule ... status".
+       orig_args="$@"
        while test $# -ne 0
        do
                case "$1" in
                -q|--quiet)
-                       quiet=1
+                       GIT_QUIET=1
                        ;;
                --cached)
                        cached=1
                        ;;
+               --recursive)
+                       recursive=1
+                       ;;
                --)
                        shift
                        break
@@ -617,22 +735,34 @@ cmd_status()
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
+               displaypath="$prefix$path"
                if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
                then
-                       say "-$sha1 $path"
+                       say "-$sha1 $displaypath"
                        continue;
                fi
                set_name_rev "$path" "$sha1"
                if git diff-files --quiet -- "$path"
                then
-                       say " $sha1 $path$revname"
+                       say " $sha1 $displaypath$revname"
                else
                        if test -z "$cached"
                        then
                                sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
                                set_name_rev "$path" "$sha1"
                        fi
-                       say "+$sha1 $path$revname"
+                       say "+$sha1 $displaypath$revname"
+               fi
+
+               if test -n "$recursive"
+               then
+                       (
+                               prefix="$displaypath/"
+                               unset GIT_DIR
+                               cd "$path" &&
+                               cmd_status $orig_args
+                       ) ||
+                       die "Failed to recurse into submodule path '$path'"
                fi
        done
 }
@@ -647,7 +777,7 @@ cmd_sync()
        do
                case "$1" in
                -q|--quiet)
-                       quiet=1
+                       GIT_QUIET=1
                        shift
                        ;;
                --)
@@ -702,7 +832,7 @@ do
                command=$1
                ;;
        -q|--quiet)
-               quiet=1
+               GIT_QUIET=1
                ;;
        -b|--branch)
                case "$2" in
index ef1d30db3889d0d33b43b1d15cd19281291f5aea..eb4b75aff347670b1817fb1c2584cff11a08fad5 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@@';
 
@@ -19,6 +19,16 @@ $ENV{GIT_DIR} ||= '.git';
 $Git::SVN::default_repo_id = 'svn';
 $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn';
 $Git::SVN::Ra::_log_window_size = 100;
+$Git::SVN::_minimize_url = 'unset';
+
+if (! exists $ENV{SVN_SSH}) {
+       if (exists $ENV{GIT_SSH}) {
+               $ENV{SVN_SSH} = $ENV{GIT_SSH};
+               if ($^O eq 'msys') {
+                       $ENV{SVN_SSH} =~ s/\\/\\\\/g;
+               }
+       }
+}
 
 $Git::SVN::Log::TZ = $ENV{TZ};
 $ENV{TZ} = 'UTC';
@@ -31,6 +41,7 @@ require SVN::Delta;
 if ($SVN::Core::VERSION lt '1.1.0') {
        fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)";
 }
+my $can_compress = eval { require Compress::Zlib; 1};
 push @Git::SVN::Ra::ISA, 'SVN::Ra';
 push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
 push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
@@ -39,6 +50,8 @@ use Digest::MD5;
 use IO::File qw//;
 use File::Basename qw/dirname basename/;
 use File::Path qw/mkpath/;
+use File::Spec;
+use File::Find;
 use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
 use IPC::Open3;
 use Git;
@@ -62,7 +75,7 @@ my ($SVN);
 $sha1 = qr/[a-f\d]{40}/;
 $sha1_short = qr/[a-f\d]{4,40}/;
 my ($_stdin, $_help, $_edit,
-       $_message, $_file,
+       $_message, $_file, $_branch_dest,
        $_template, $_shared,
        $_version, $_fetch_all, $_no_rebase, $_fetch_parent,
        $_merge, $_strategy, $_dry_run, $_local,
@@ -76,6 +89,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,
@@ -90,13 +104,13 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
                'localtime' => \$Git::SVN::_localtime,
                %remote_opts );
 
-my ($_trunk, $_tags, $_branches, $_stdlayout);
+my ($_trunk, @_tags, @_branches, $_stdlayout);
 my %icv;
 my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared,
-                  'trunk|T=s' => \$_trunk, 'tags|t=s' => \$_tags,
-                  'branches|b=s' => \$_branches, 'prefix=s' => \$_prefix,
+                  'trunk|T=s' => \$_trunk, 'tags|t=s@' => \@_tags,
+                  'branches|b=s@' => \@_branches, 'prefix=s' => \$_prefix,
                   'stdlayout|s' => \$_stdlayout,
-                  'minimize-url|m' => \$Git::SVN::_minimize_url,
+                  'minimize-url|m!' => \$Git::SVN::_minimize_url,
                  'no-metadata' => sub { $icv{noMetadata} = 1 },
                  'use-svm-props' => sub { $icv{useSvmProps} = 1 },
                  'use-svnsync-props' => sub { $icv{useSvnsyncProps} = 1 },
@@ -139,11 +153,13 @@ my %cmd = (
        branch => [ \&cmd_branch,
                    'Create a branch in the SVN repository',
                    { 'message|m=s' => \$_message,
+                     'destination|d=s' => \$_branch_dest,
                      'dry-run|n' => \$_dry_run,
                      'tag|t' => \$_tag } ],
        tag => [ sub { $_tag = 1; cmd_branch(@_) },
                 'Create a tag in the SVN repository',
                 { 'message|m=s' => \$_message,
+                  'destination|d=s' => \$_branch_dest,
                   'dry-run|n' => \$_dry_run } ],
        'set-tree' => [ \&cmd_set_tree,
                        "Set an SVN repository to a git tree-ish",
@@ -209,6 +225,14 @@ my %cmd = (
        'blame' => [ \&Git::SVN::Log::cmd_blame,
                    "Show what revision and author last modified each line of a file",
                    { 'git-format' => \$_git_format } ],
+       'reset' => [ \&cmd_reset,
+                    "Undo fetches back to the specified SVN revision",
+                    { 'revision|r=s' => \$_revision,
+                      'parent|p' => \$_fetch_parent } ],
+       'gc' => [ \&cmd_gc,
+                 "Compress unhandled.log files in .git/svn and remove " .
+                 "index files in .git/svn",
+               {} ],
 );
 
 my $cmd;
@@ -217,6 +241,9 @@ for (my $i = 0; $i < @ARGV; $i++) {
                $cmd = $ARGV[$i];
                splice @ARGV, $i, 1;
                last;
+       } elsif ($ARGV[$i] eq 'help') {
+               $cmd = $ARGV[$i+1];
+               usage(0);
        }
 };
 
@@ -263,6 +290,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();
@@ -353,7 +383,7 @@ sub init_subdir {
 sub cmd_clone {
        my ($url, $path) = @_;
        if (!defined $path &&
-           (defined $_trunk || defined $_branches || defined $_tags ||
+           (defined $_trunk || @_branches || @_tags ||
             defined $_stdlayout) &&
            $url !~ m#^[a-z\+]+://#) {
                $path = $url;
@@ -361,22 +391,28 @@ 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 {
        if (defined $_stdlayout) {
                $_trunk = 'trunk' if (!defined $_trunk);
-               $_tags = 'tags' if (!defined $_tags);
-               $_branches = 'branches' if (!defined $_branches);
+               @_tags = 'tags' if (! @_tags);
+               @_branches = 'branches' if (! @_branches);
        }
-       if (defined $_trunk || defined $_branches || defined $_tags) {
+       if (defined $_trunk || @_branches || @_tags) {
                return cmd_multi_init(@_);
        }
        my $url = shift or die "SVN repository location required ",
                               "as a command-line argument\n";
+       $url = canonicalize_url($url);
        init_subdir(@_);
        do_git_init_db();
 
+       if ($Git::SVN::_minimize_url eq 'unset') {
+               $Git::SVN::_minimize_url = 0;
+       }
+
        Git::SVN->init($url);
 }
 
@@ -448,8 +484,22 @@ sub cmd_dcommit {
                'Cannot dcommit with a dirty index.  Commit your changes first, '
                . "or stash them with `git stash'.\n";
        $head ||= 'HEAD';
+
+       my $old_head;
+       if ($head ne 'HEAD') {
+               $old_head = eval {
+                       command_oneline([qw/symbolic-ref -q HEAD/])
+               };
+               if ($old_head) {
+                       $old_head =~ s{^refs/heads/}{};
+               } else {
+                       $old_head = eval { command_oneline(qw/rev-parse HEAD/) };
+               }
+               command(['checkout', $head], STDERR => 0);
+       }
+
        my @refs;
-       my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
+       my ($url, $rev, $uuid, $gs) = working_head_info('HEAD', \@refs);
        unless ($gs) {
                die "Unable to determine upstream SVN information from ",
                    "$head history.\nPerhaps the repository is empty.";
@@ -535,7 +585,7 @@ sub cmd_dcommit {
                        if (@diff) {
                                @refs = ();
                                my ($url_, $rev_, $uuid_, $gs_) =
-                                             working_head_info($head, \@refs);
+                                             working_head_info('HEAD', \@refs);
                                my ($linear_refs_, $parents_) =
                                              linearize_history($gs_, \@refs);
                                if (scalar(@$linear_refs) !=
@@ -553,8 +603,15 @@ sub cmd_dcommit {
                                          "\nBefore dcommitting";
                                }
                                if ($url_ ne $expect_url) {
-                                       fatal "URL mismatch after rebase: ",
-                                             "$url_ != $expect_url";
+                                       if ($url_ eq $gs->metadata_url) {
+                                               print
+                                                 "Accepting rewritten URL:",
+                                                 " $url_\n";
+                                       } else {
+                                               fatal
+                                                 "URL mismatch after rebase:",
+                                                 " $url_ != $expect_url";
+                                       }
                                }
                                if ($uuid_ ne $uuid) {
                                        fatal "uuid mismatch after rebase: ",
@@ -573,6 +630,22 @@ sub cmd_dcommit {
                        }
                }
        }
+
+       if ($old_head) {
+               my $new_head = command_oneline(qw/rev-parse HEAD/);
+               my $new_is_symbolic = eval {
+                       command_oneline(qw/symbolic-ref -q HEAD/);
+               };
+               if ($new_is_symbolic) {
+                       print "dcommitted the branch ", $head, "\n";
+               } else {
+                       print "dcommitted on a detached HEAD because you gave ",
+                             "a revision argument.\n",
+                             "The rewritten commit is: ", $new_head, "\n";
+               }
+               command(['checkout', $old_head], STDERR => 0);
+       }
+
        unlink $gs->{index};
 }
 
@@ -587,7 +660,46 @@ sub cmd_branch {
        my ($src, $rev, undef, $gs) = working_head_info($head);
 
        my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
-       my $glob = $remote->{ $_tag ? 'tags' : 'branches' };
+       my $allglobs = $remote->{ $_tag ? 'tags' : 'branches' };
+       my $glob;
+       if ($#{$allglobs} == 0) {
+               $glob = $allglobs->[0];
+       } else {
+               unless(defined $_branch_dest) {
+                       die "Multiple ",
+                           $_tag ? "tag" : "branch",
+                           " paths defined for Subversion repository.\n",
+                           "You must specify where you want to create the ",
+                           $_tag ? "tag" : "branch",
+                           " with the --destination argument.\n";
+               }
+               foreach my $g (@{$allglobs}) {
+                       # SVN::Git::Editor could probably be moved to Git.pm..
+                       my $re = SVN::Git::Editor::glob2pat($g->{path}->{left});
+                       if ($_branch_dest =~ /$re/) {
+                               $glob = $g;
+                               last;
+                       }
+               }
+               unless (defined $glob) {
+                       my $dest_re = qr/\b\Q$_branch_dest\E\b/;
+                       foreach my $g (@{$allglobs}) {
+                               $g->{path}->{left} =~ /$dest_re/ or next;
+                               if (defined $glob) {
+                                       die "Ambiguous destination: ",
+                                           $_branch_dest, "\nmatches both '",
+                                           $glob->{path}->{left}, "' and '",
+                                           $g->{path}->{left}, "'\n";
+                               }
+                               $glob = $g;
+                       }
+                       unless (defined $glob) {
+                               die "Unknown ",
+                                   $_tag ? "tag" : "branch",
+                                   " destination $_branch_dest\n";
+                       }
+               }
+       }
        my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/};
        my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ());
 
@@ -668,6 +780,7 @@ sub cmd_show_ignore {
                print STDOUT "\n# $path\n";
                my $s = $props->{'svn:ignore'} or return;
                $s =~ s/[\r\n]+/\n/g;
+               $s =~ s/^\n+//;
                chomp $s;
                $s =~ s#^#$path#gm;
                print STDOUT "$s\n";
@@ -705,6 +818,7 @@ sub cmd_create_ignore {
                open(GITIGNORE, '>', $ignore)
                  or fatal("Failed to open `$ignore' for writing: $!");
                $s =~ s/[\r\n]+/\n/g;
+               $s =~ s/^\n+//;
                chomp $s;
                # Prefix all patterns so that the ignore doesn't apply
                # to sub-directories.
@@ -735,6 +849,12 @@ sub canonicalize_path {
        return $path;
 }
 
+sub canonicalize_url {
+       my ($url) = @_;
+       $url =~ s#^([^:]+://[^/]*/)(.*)$#$1 . canonicalize_path($2)#e;
+       return $url;
+}
+
 # get_svnprops(PATH)
 # ------------------
 # Helper for cmd_propget and cmd_proplist below.
@@ -794,22 +914,18 @@ sub cmd_proplist {
 
 sub cmd_multi_init {
        my $url = shift;
-       unless (defined $_trunk || defined $_branches || defined $_tags) {
+       unless (defined $_trunk || @_branches || @_tags) {
                usage(1);
        }
 
-       # there are currently some bugs that prevent multi-init/multi-fetch
-       # setups from working well without this.
-       $Git::SVN::_minimize_url = 1;
-
        $_prefix = '' unless defined $_prefix;
        if (defined $url) {
-               $url =~ s#/+$##;
+               $url = canonicalize_url($url);
                init_subdir(@_);
        }
        do_git_init_db();
        if (defined $_trunk) {
-               my $trunk_ref = $_prefix . 'trunk';
+               my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk';
                # try both old-style and new-style lookups:
                my $gs_trunk = eval { Git::SVN->new($trunk_ref) };
                unless ($gs_trunk) {
@@ -819,10 +935,14 @@ sub cmd_multi_init {
                                                   undef, $trunk_ref);
                }
        }
-       return unless defined $_branches || defined $_tags;
+       return unless @_branches || @_tags;
        my $ra = $url ? Git::SVN::Ra->new($url) : undef;
-       complete_url_ls_init($ra, $_branches, '--branches/-b', $_prefix);
-       complete_url_ls_init($ra, $_tags, '--tags/-t', $_prefix . 'tags/');
+       foreach my $path (@_branches) {
+               complete_url_ls_init($ra, $path, '--branches/-b', $_prefix);
+       }
+       foreach my $path (@_tags) {
+               complete_url_ls_init($ra, $path, '--tags/-t', $_prefix.'tags/');
+       }
 }
 
 sub cmd_multi_fetch {
@@ -1015,6 +1135,28 @@ sub cmd_info {
        print $result, "\n";
 }
 
+sub cmd_reset {
+       my $target = shift || $_revision or die "SVN revision required\n";
+       $target = $1 if $target =~ /^r(\d+)$/;
+       $target =~ /^\d+$/ or die "Numeric SVN revision expected\n";
+       my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+       unless ($gs) {
+               die "Unable to determine upstream SVN information from ".
+                   "history\n";
+       }
+       my ($r, $c) = $gs->find_rev_before($target, not $_fetch_parent);
+       $gs->rev_map_set($r, $c, 'reset', $uuid);
+       print "r$r = $c ($gs->{ref_id})\n";
+}
+
+sub cmd_gc {
+       if (!$can_compress) {
+               warn "Compress::Zlib could not be found; unhandled.log " .
+                    "files will not be compressed.\n";
+       }
+       find({ wanted => \&gc_directory, no_chdir => 1}, "$ENV{GIT_DIR}/svn");
+}
+
 ########################### utility functions #########################
 
 sub rebase_cmd {
@@ -1030,6 +1172,17 @@ sub post_fetch_checkout {
        my $gs = $Git::SVN::_head or return;
        return if verify_ref('refs/heads/master^0');
 
+       # look for "trunk" ref if it exists
+       my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
+       my $fetch = $remote->{fetch};
+       if ($fetch) {
+               foreach my $p (keys %$fetch) {
+                       basename($fetch->{$p}) eq 'trunk' or next;
+                       $gs = Git::SVN->new($fetch->{$p}, $gs->{repo_id}, $p);
+                       last;
+               }
+       }
+
        my $valid_head = verify_ref('HEAD^0');
        command_noisy(qw(update-ref refs/heads/master), $gs->refname);
        return if ($valid_head || !verify_ref('HEAD^0'));
@@ -1084,7 +1237,8 @@ sub complete_url_ls_init {
                    "wanted to set to: $gs->{url}\n";
        }
        command_oneline('config', $k, $gs->{url}) unless $orig_url;
-       my $remote_path = "$ra->{svn_path}/$repo_path";
+       my $remote_path = "$gs->{path}/$repo_path";
+       $remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
        $remote_path =~ s#/+#/#g;
        $remote_path =~ s#^/##g;
        $remote_path .= "/*" if $remote_path !~ /\*/;
@@ -1093,6 +1247,7 @@ sub complete_url_ls_init {
                die "--prefix='$pfx' must have a trailing slash '/'\n";
        }
        command_noisy('config',
+                     '--add',
                      "svn-remote.$gs->{repo_id}.$n",
                      "$remote_path:refs/remotes/$pfx*" .
                        ('/*' x (($remote_path =~ tr/*/*/) - 1)) );
@@ -1172,16 +1327,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;
@@ -1255,11 +1421,11 @@ sub read_repo_config {
 sub extract_metadata {
        my $id = shift or return (undef, undef, undef);
        my ($url, $rev, $uuid) = ($id =~ /^\s*git-svn-id:\s+(.*)\@(\d+)
-                                                       \s([a-f\d\-]+)$/x);
+                                                       \s([a-f\d\-]+)$/ix);
        if (!defined $rev || !$uuid || !$url) {
                # some of the original repositories I made had
                # identifiers like this:
-               ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/);
+               ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/i);
        }
        return ($url, $rev, $uuid);
 }
@@ -1423,6 +1589,25 @@ sub md5sum {
        return $md5->hexdigest();
 }
 
+sub gc_directory {
+       if ($can_compress && -f $_ && basename($_) eq "unhandled.log") {
+               my $out_filename = $_ . ".gz";
+               open my $in_fh, "<", $_ or die "Unable to open $_: $!\n";
+               binmode $in_fh;
+               my $gz = Compress::Zlib::gzopen($out_filename, "ab") or
+                               die "Unable to open $out_filename: $!\n";
+
+               my $res;
+               while ($res = sysread($in_fh, my $str, 1024)) {
+                       $gz->gzwrite($str) or
+                               die "Unable to write: ".$gz->gzerror()."!\n";
+               }
+               unlink $_ or die "unlink $File::Find::name: $!\n";
+       } elsif (-f $_ && basename($_) eq "index") {
+               unlink $_ or die "unlink $_: $!\n";
+       }
+}
+
 package Git::SVN;
 use strict;
 use warnings;
@@ -1486,23 +1671,23 @@ sub resolve_local_globs {
        return unless defined $glob_spec;
        my $ref = $glob_spec->{ref};
        my $path = $glob_spec->{path};
-       foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
-               next unless m#^refs/remotes/$ref->{regex}$#;
+       foreach (command(qw#for-each-ref --format=%(refname) refs/#)) {
+               next unless m#^$ref->{regex}$#;
                my $p = $1;
                my $pathname = desanitize_refname($path->full_path($p));
                my $refname = desanitize_refname($ref->full_path($p));
                if (my $existing = $fetch->{$pathname}) {
                        if ($existing ne $refname) {
                                die "Refspec conflict:\n",
-                                   "existing: refs/remotes/$existing\n",
-                                   " globbed: refs/remotes/$refname\n";
+                                   "existing: $existing\n",
+                                   " globbed: $refname\n";
                        }
-                       my $u = (::cmt_metadata("refs/remotes/$refname"))[0];
+                       my $u = (::cmt_metadata("$refname"))[0];
                        $u =~ s!^\Q$url\E(/|$)!! or die
-                         "refs/remotes/$refname: '$url' not found in '$u'\n";
+                         "$refname: '$url' not found in '$u'\n";
                        if ($pathname ne $u) {
                                warn "W: Refspec glob conflict ",
-                                    "(ref: refs/remotes/$refname):\n",
+                                    "(ref: $refname):\n",
                                     "expected path: $pathname\n",
                                     "    real path: $u\n",
                                     "Continuing ahead with $u\n";
@@ -1543,12 +1728,14 @@ sub fetch_all {
        my $ra = Git::SVN::Ra->new($url);
        my $uuid = $ra->get_uuid;
        my $head = $ra->get_latest_revnum;
+       $ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] });
        my $base = defined $fetch ? $head : 0;
 
        # read the max revs for wildcard expansion (branches/*, tags/*)
        foreach my $t (qw/branches tags/) {
                defined $remote->{$t} or next;
-               push @globs, $remote->{$t};
+               push @globs, @{$remote->{$t}};
+
                my $max_rev = eval { tmp_config(qw/--int --get/,
                                         "svn-remote.$repo_id.${t}-maxRev") };
                if (defined $max_rev && ($max_rev < $base)) {
@@ -1578,32 +1765,35 @@ sub read_all_remotes {
        my $use_svm_props = eval { command_oneline(qw/config --bool
            svn.useSvmProps/) };
        $use_svm_props = $use_svm_props eq 'true' if $use_svm_props;
+       my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*};
        foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
-               if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) {
-                       my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3);
-                       die("svn-remote.$remote: remote ref '$_remote_ref' "
-                           . "must start with 'refs/remotes/'\n")
-                               unless $_remote_ref =~ m{^refs/remotes/(.+)};
-                       my $remote_ref = $1;
-                       $local_ref =~ s{^/}{};
+               if (m!^(.+)\.fetch=$svn_refspec$!) {
+                       my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
+                       die("svn-remote.$remote: remote ref '$remote_ref' "
+                           . "must start with 'refs/'\n")
+                               unless $remote_ref =~ m{^refs/};
                        $r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
                        $r->{$remote}->{svm} = {} if $use_svm_props;
                } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
                        $r->{$1}->{svm} = {};
                } elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
                        $r->{$1}->{url} = $2;
-               } elsif (m!^(.+)\.(branches|tags)=
-                          (.*):refs/remotes/(.+)\s*$/!x) {
-                       my ($p, $g) = ($3, $4);
-                       my $rs = $r->{$1}->{$2} = {
-                                         t => $2,
-                                         remote => $1,
-                                         path => Git::SVN::GlobSpec->new($p),
-                                         ref => Git::SVN::GlobSpec->new($g) };
+               } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) {
+                       my ($remote, $t, $local_ref, $remote_ref) =
+                                                            ($1, $2, $3, $4);
+                       die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
+                           . "must start with 'refs/'\n")
+                               unless $remote_ref =~ m{^refs/};
+                       my $rs = {
+                           t => $t,
+                           remote => $remote,
+                           path => Git::SVN::GlobSpec->new($local_ref),
+                           ref => Git::SVN::GlobSpec->new($remote_ref) };
                        if (length($rs->{ref}->{right}) != 0) {
                                die "The '*' glob character must be the last ",
-                                   "character of '$g'\n";
+                                   "character of '$remote_ref'\n";
                        }
+                       push @{ $r->{$remote}->{$t} }, $rs;
                }
        }
 
@@ -1711,14 +1901,15 @@ sub init_remote_config {
                }
        }
        my ($xrepo_id, $xpath) = find_ref($self->refname);
-       if (defined $xpath) {
+       if (!$no_write && defined $xpath) {
                die "svn-remote.$xrepo_id.fetch already set to track ",
-                   "$xpath:refs/remotes/", $self->refname, "\n";
+                   "$xpath:", $self->refname, "\n";
        }
        unless ($no_write) {
                command_noisy('config',
                              "svn-remote.$self->{repo_id}.url", $url);
                $self->{path} =~ s{^/}{};
+               $self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
                command_noisy('config', '--add',
                              "svn-remote.$self->{repo_id}.fetch",
                              "$self->{path}:".$self->refname);
@@ -1743,9 +1934,10 @@ sub find_by_url { # repos_root and, path are optional
                next if defined $repos_root && $repos_root ne $u;
 
                my $fetch = $remotes->{$repo_id}->{fetch} || {};
-               foreach (qw/branches tags/) {
-                       resolve_local_globs($u, $fetch,
-                                           $remotes->{$repo_id}->{$_});
+               foreach my $t (qw/branches tags/) {
+                       foreach my $globspec (@{$remotes->{$repo_id}->{$t}}) {
+                               resolve_local_globs($u, $fetch, $globspec);
+                       }
                }
                my $p = $path;
                my $rwr = rewrite_root({repo_id => $repo_id});
@@ -1787,7 +1979,7 @@ sub find_ref {
        my ($ref_id) = @_;
        foreach (command(qw/config -l/)) {
                next unless m!^svn-remote\.(.+)\.fetch=
-                             \s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x;
+                             \s*/?(.*?)\s*:\s*(.+?)\s*$!x;
                my ($repo_id, $path, $ref) = ($1, $2, $3);
                if ($ref eq $ref_id) {
                        $path = '' if ($path =~ m#^\./?#);
@@ -1804,16 +1996,16 @@ sub new {
                if (!defined $repo_id) {
                        die "Could not find a \"svn-remote.*.fetch\" key ",
                            "in the repository configuration matching: ",
-                           "refs/remotes/$ref_id\n";
+                           "$ref_id\n";
                }
        }
        my $self = _new($class, $repo_id, $ref_id, $path);
        if (!defined $self->{path} || !length $self->{path}) {
                my $fetch = command_oneline('config', '--get',
                                            "svn-remote.$repo_id.fetch",
-                                           ":refs/remotes/$ref_id\$") or
+                                           ":$ref_id\$") or
                     die "Failed to read \"svn-remote.$repo_id.fetch\" ",
-                        "\":refs/remotes/$ref_id\$\" in config\n";
+                        "\":$ref_id\$\" in config\n";
                ($self->{path}, undef) = split(/\s*:\s*/, $fetch);
        }
        $self->{url} = command_oneline('config', '--get',
@@ -1824,7 +2016,7 @@ sub new {
 }
 
 sub refname {
-       my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
+       my ($refname) = $_[0]->{ref_id} ;
 
        # It cannot end with a slash /, we'll throw up on this because
        # SVN can't have directories with a slash in their name, either:
@@ -1903,7 +2095,7 @@ sub _set_svm_vars {
 
                chomp($src, $uuid);
 
-               $uuid =~ m{^[0-9a-f\-]{30,}$}
+               $uuid =~ m{^[0-9a-f\-]{30,}$}i
                    or die "doesn't look right - svm:uuid is '$uuid'\n";
 
                # the '!' is used to mark the repos_root!/relative/path
@@ -1989,7 +2181,7 @@ sub svnsync {
                   die "doesn't look right - svn:sync-from-url is '$url'\n";
 
                my $uuid = tmp_config('--get', "$section.svnsync-uuid");
-               ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}) or
+               ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}i) or
                   die "doesn't look right - svn:sync-from-uuid is '$uuid'\n";
 
                $svnsync = { url => $url, uuid => $uuid }
@@ -2007,7 +2199,7 @@ sub svnsync {
                   die "doesn't look right - svn:sync-from-url is '$url'\n";
 
        my $uuid = $rp->{'svn:sync-from-uuid'} or die $err . "uuid\n";
-       ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}) or
+       ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}i) or
                   die "doesn't look right - svn:sync-from-uuid is '$uuid'\n";
 
        my $section = "svn-remote.$self->{repo_id}";
@@ -2023,7 +2215,7 @@ sub ra_uuid {
        unless ($self->{ra_uuid}) {
                my $key = "svn-remote.$self->{repo_id}.uuid";
                my $uuid = eval { tmp_config('--get', $key) };
-               if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/) {
+               if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/i) {
                        $self->{ra_uuid} = $uuid;
                } else {
                        die "ra_uuid called without URL\n" unless $self->{url};
@@ -2066,16 +2258,6 @@ sub ra {
        $ra;
 }
 
-sub rel_path {
-       my ($self) = @_;
-       my $repos_root = $self->ra->{repos_root};
-       return $self->{path} if ($self->{url} eq $repos_root);
-       my $url = $self->{url} .
-                 (length $self->{path} ? "/$self->{path}" : $self->{path});
-       $url =~ s!^\Q$repos_root\E(?:/+|$)!!g;
-       $url;
-}
-
 # prop_walk(PATH, REV, SUB)
 # -------------------------
 # Recursively traverse PATH at revision REV and invoke SUB for each
@@ -2401,10 +2583,7 @@ sub match_paths {
        if (my $path = $paths->{"/$self->{path}"}) {
                return ($path->{action} eq 'D') ? 0 : 1;
        }
-       my $repos_root = $self->ra->{repos_root};
-       my $extended_path = $self->{url} . '/' . $self->{path};
-       $extended_path =~ s#^\Q$repos_root\E(/|$)##;
-       $self->{path_regex} ||= qr/^\/\Q$extended_path\E\//;
+       $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//;
        if (grep /$self->{path_regex}/, keys %$paths) {
                return 1;
        }
@@ -2427,15 +2606,14 @@ sub find_parent_branch {
        unless (defined $paths) {
                my $err_handler = $SVN::Error::handler;
                $SVN::Error::handler = \&Git::SVN::Ra::skip_unknown_revs;
-               $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1, sub {
-                                  $paths =
-                                     Git::SVN::Ra::dup_changed_paths($_[0]) });
+               $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1,
+                                  sub { $paths = $_[0] });
                $SVN::Error::handler = $err_handler;
        }
        return undef unless defined $paths;
 
        # look for a parent from another branch:
-       my @b_path_components = split m#/#, $self->rel_path;
+       my @b_path_components = split m#/#, $self->{path};
        my @a_path_components;
        my $i;
        while (@b_path_components) {
@@ -2453,11 +2631,12 @@ sub find_parent_branch {
        my $r = $i->{copyfrom_rev};
        my $repos_root = $self->ra->{repos_root};
        my $url = $self->ra->{url};
-       my $new_url = $repos_root . $branch_from;
+       my $new_url = $url . $branch_from;
        print STDERR  "Found possible branch point: ",
-                     "$new_url => ", $self->full_url, ", $r\n";
+                     "$new_url => ", $self->full_url, ", $r\n"
+                     unless $::_q > 1;
        $branch_from =~ s#^/##;
-       my $gs = $self->other_gs($new_url, $url, $repos_root,
+       my $gs = $self->other_gs($new_url, $url,
                                 $branch_from, $r, $self->{ref_id});
        my ($r0, $parent) = $gs->find_rev_before($r, 1);
        {
@@ -2476,11 +2655,13 @@ sub find_parent_branch {
                ($r0, $parent) = $gs->find_rev_before($r, 1);
        }
        if (defined $r0 && defined $parent) {
-               print STDERR "Found branch parent: ($self->{ref_id}) $parent\n";
+               print STDERR "Found branch parent: ($self->{ref_id}) $parent\n"
+                            unless $::_q > 1;
                my $ed;
                if ($self->ra->can_do_switch) {
                        $self->assert_index_clean($parent);
-                       print STDERR "Following parent with do_switch\n";
+                       print STDERR "Following parent with do_switch\n"
+                                    unless $::_q > 1;
                        # do_switch works with svn/trunk >= r22312, but that
                        # is not included with SVN 1.4.3 (the latest version
                        # at the moment), so we can't rely on it
@@ -2495,18 +2676,20 @@ sub find_parent_branch {
                        print STDERR "Trees match:\n",
                                     "  $new_url\@$r0\n",
                                     "  ${\$self->full_url}\@$rev\n",
-                                    "Following parent with no changes\n";
+                                    "Following parent with no changes\n"
+                                    unless $::_q > 1;
                        $self->tmp_index_do(sub {
                            command_noisy('read-tree', $parent);
                        });
                        $self->{last_commit} = $parent;
                } else {
-                       print STDERR "Following parent with do_update\n";
+                       print STDERR "Following parent with do_update\n"
+                                    unless $::_q > 1;
                        $ed = SVN::Git::Fetcher->new($self);
                        $self->ra->gs_do_update($rev, $rev, $self, $ed)
                          or die "SVN connection failed somewhere...\n";
                }
-               print STDERR "Successfully followed parent\n";
+               print STDERR "Successfully followed parent\n" unless $::_q > 1;
                return $self->make_log_entry($rev, [$parent], $ed);
        }
        return undef;
@@ -2642,16 +2825,16 @@ sub parse_svn_date {
 }
 
 sub other_gs {
-       my ($self, $new_url, $url, $repos_root,
+       my ($self, $new_url, $url,
            $branch_from, $r, $old_ref_id) = @_;
-       my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from);
+       my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from);
        unless ($gs) {
                my $ref_id = $old_ref_id;
                $ref_id =~ s/\@\d+$//;
                $ref_id .= "\@$r";
                # just grow a tail if we're not unique enough :x
                $ref_id .= '-' while find_ref($ref_id);
-               print STDERR "Initializing parent: $ref_id\n";
+               print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
                my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
                if ($u =~ s#^\Q$url\E(/|$)##) {
                        $p = $u;
@@ -2663,12 +2846,34 @@ sub other_gs {
        $gs
 }
 
+sub call_authors_prog {
+       my ($orig_author) = @_;
+       $orig_author = command_oneline('rev-parse', '--sq-quote', $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;
 }
@@ -2734,7 +2939,7 @@ sub make_log_entry {
                        die "Can't have both 'useSvmProps' and 'rewriteRoot' ",
                            "options set!\n";
                }
-               my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$};
+               my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$}i;
                # we don't want "SVM: initializing mirror for junk" ...
                return undef if $r == 0;
                my $svm = $self->svm;
@@ -2952,6 +3157,14 @@ sub _rev_map_set {
          croak "write: $!";
 }
 
+sub _rev_map_reset {
+       my ($fh, $rev, $commit) = @_;
+       my $c = _rev_map_get($fh, $rev);
+       $c eq $commit or die "_rev_map_reset(@_) commit $c does not match!\n";
+       my $offset = sysseek($fh, 0, SEEK_CUR) or croak "seek: $!";
+       truncate $fh, $offset or croak "truncate: $!";
+}
+
 sub mkfile {
        my ($path) = @_;
        unless (-e $path) {
@@ -2968,6 +3181,7 @@ sub rev_map_set {
        my $db = $self->map_path($uuid);
        my $db_lock = "$db.lock";
        my $sig;
+       $update_ref ||= 0;
        if ($update_ref) {
                $SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
                            $SIG{USR1} = $SIG{USR2} = sub { $sig = $_[0] };
@@ -2991,7 +3205,8 @@ sub rev_map_set {
 
        sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
             or croak "Couldn't open $db_lock: $!\n";
-       _rev_map_set($fh, $rev, $commit);
+       $update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
+                                _rev_map_set($fh, $rev, $commit);
        if ($sync) {
                $fh->flush or die "Couldn't flush $db_lock: $!\n";
                $fh->sync or die "Couldn't sync $db_lock: $!\n";
@@ -2999,7 +3214,9 @@ sub rev_map_set {
        close $fh or croak $!;
        if ($update_ref) {
                $_head = $self;
-               command_noisy('update-ref', '-m', "r$rev",
+               my $note = "";
+               $note = " ($update_ref)" if ($update_ref !~ /^\d*$/);
+               command_noisy('update-ref', '-m', "r$rev$note",
                              $self->refname, $commit);
        }
        rename $db_lock, $db or die "rev_map_set(@_): ", "Failed to rename: ",
@@ -3061,12 +3278,19 @@ sub rev_map_get {
        return undef unless -e $map_path;
 
        sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
+       my $c = _rev_map_get($fh, $rev);
+       close($fh) or croak "close: $!";
+       $c
+}
+
+sub _rev_map_get {
+       my ($fh, $rev) = @_;
+
        binmode $fh or croak "binmode: $!";
        my $size = (stat($fh))[7];
        ($size % 24) == 0 or croak "inconsistent size: $size";
 
        if ($size == 0) {
-               close $fh or croak "close: $fh";
                return undef;
        }
 
@@ -3077,18 +3301,16 @@ sub rev_map_get {
                my $i = int(($l/24 + $u/24) / 2) * 24;
                sysseek($fh, $i, SEEK_SET) or croak "seek: $!";
                sysread($fh, my $buf, 24) == 24 or croak "read: $!";
-               my ($r, $c) = unpack('NH40', $buf);
+               my ($r, $c) = unpack(rev_map_fmt, $buf);
 
                if ($r < $rev) {
                        $l = $i + 24;
                } elsif ($r > $rev) {
                        $u = $i - 24;
                } else { # $r == $rev
-                       close($fh) or croak "close: $!";
                        return $c eq ('0' x 40) ? undef : $c;
                }
        }
-       close($fh) or croak "close: $!";
        undef;
 }
 
@@ -3100,6 +3322,8 @@ sub find_rev_before {
        my ($self, $rev, $eq_ok, $min_rev) = @_;
        --$rev unless $eq_ok;
        $min_rev ||= 1;
+       my $max_rev = $self->rev_map_max;
+       $rev = $max_rev if ($rev > $max_rev);
        while ($rev >= $min_rev) {
                if (my $c = $self->rev_map_get($rev)) {
                        return ($rev, $c);
@@ -3132,12 +3356,24 @@ sub _new {
                $repo_id = $Git::SVN::default_repo_id;
        }
        unless (defined $ref_id && length $ref_id) {
-               $_[2] = $ref_id = $Git::SVN::default_ref_id;
+               $_prefix = '' unless defined($_prefix);
+               $_[2] = $ref_id =
+                            "refs/remotes/$_prefix$Git::SVN::default_ref_id";
        }
        $_[1] = $repo_id;
        my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
+
+       # Older repos imported by us used $GIT_DIR/svn/foo instead of
+       # $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo
+       if ($ref_id =~ m{^refs/remotes/(.*)}) {
+               my $old_dir = "$ENV{GIT_DIR}/svn/$1";
+               if (-d $old_dir && ! -d $dir) {
+                       $dir = $old_dir;
+               }
+       }
+
        $_[3] = $path = '' unless (defined $path);
-       mkpath(["$ENV{GIT_DIR}/svn"]);
+       mkpath([$dir]);
        bless {
                ref_id => $ref_id, dir => $dir, index => "$dir/index",
                path => $path, config => "$ENV{GIT_DIR}/svn/config",
@@ -3820,7 +4056,7 @@ sub repo_path {
 sub url_path {
        my ($self, $path) = @_;
        if ($self->{url} =~ m#^https?://#) {
-               $path =~ s/([^~a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
+               $path =~ s!([^~a-zA-Z0-9_./-])!uc sprintf("%%%02x",ord($1))!eg;
        }
        $self->{url} . '/' . $self->repo_path($path);
 }
@@ -4280,6 +4516,34 @@ sub get_log {
        my ($self, @args) = @_;
        my $pool = SVN::Pool->new;
 
+       # svn_log_changed_path_t objects passed to get_log are likely to be
+       # overwritten even if only the refs are copied to an external variable,
+       # so we should dup the structures in their entirety.  Using an
+       # externally passed pool (instead of our temporary and quickly cleared
+       # pool in Git::SVN::Ra) does not help matters at all...
+       my $receiver = pop @args;
+       my $prefix = "/".$self->{svn_path};
+       $prefix =~ s#/+($)##;
+       my $prefix_regex = qr#^\Q$prefix\E#;
+       push(@args, sub {
+               my ($paths) = $_[0];
+               return &$receiver(@_) unless $paths;
+               $_[0] = ();
+               foreach my $p (keys %$paths) {
+                       my $i = $paths->{$p};
+                       # Make path relative to our url, not repos_root
+                       $p =~ s/$prefix_regex//;
+                       my %s = map { $_ => $i->$_; }
+                               qw/copyfrom_path copyfrom_rev action/;
+                       if ($s{'copyfrom_path'}) {
+                               $s{'copyfrom_path'} =~ s/$prefix_regex//;
+                       }
+                       $_[0]{$p} = \%s;
+               }
+               &$receiver(@_);
+       });
+
+
        # the limit parameter was not supported in SVN 1.1.x, so we
        # drop it.  Therefore, the receiver callback passed to it
        # is made aware of this limitation by being wrapped if
@@ -4364,10 +4628,12 @@ sub gs_do_switch {
 
        my $full_url = $self->{url};
        my $old_url = $full_url;
-       $full_url .= '/' . escape_uri_only($path) if length $path;
+       $full_url .= '/' . $path if length $path;
        my ($ra, $reparented);
 
-       if ($old_url =~ m#^svn(\+ssh)?://#) {
+       if ($old_url =~ m#^svn(\+ssh)?://# ||
+           ($full_url =~ m#^https?://# &&
+            escape_url($full_url) ne $full_url)) {
                $_[0] = undef;
                $self = undef;
                $RA = undef;
@@ -4438,6 +4704,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;
@@ -4448,15 +4715,17 @@ sub gs_fetch_loop_common {
                };
                sub _cb {
                        my ($paths, $r, $author, $date, $log) = @_;
-                       [ dup_changed_paths($paths),
+                       [ $paths,
                          { author => $author, date => $date, log => $log } ];
                }
                $self->get_log([$longest_path], $min, $max, 0, 1, 1,
                               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 +4737,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;
 
@@ -4612,7 +4882,11 @@ sub minimize_url {
        my $c = '';
        do {
                $url .= "/$c" if length $c;
-               eval { (ref $self)->new($url)->get_latest_revnum };
+               eval {
+                       my $ra = (ref $self)->new($url);
+                       my $latest = $ra->get_latest_revnum;
+                       $ra->get_log("", $latest, 0, 1, 0, 1, sub {});
+               };
        } while ($@ && ($c = shift @components));
        $url;
 }
@@ -4668,24 +4942,6 @@ sub skip_unknown_revs {
        die "Error from SVN, ($errno): ", $err->expanded_message,"\n";
 }
 
-# svn_log_changed_path_t objects passed to get_log are likely to be
-# overwritten even if only the refs are copied to an external variable,
-# so we should dup the structures in their entirety.  Using an externally
-# passed pool (instead of our temporary and quickly cleared pool in
-# Git::SVN::Ra) does not help matters at all...
-sub dup_changed_paths {
-       my ($paths) = @_;
-       return undef unless $paths;
-       my %ret;
-       foreach my $p (keys %$paths) {
-               my $i = $paths->{$p};
-               my %s = map { $_ => $i->$_ }
-                             qw/copyfrom_path copyfrom_rev action/;
-               $ret{$p} = \%s;
-       }
-       \%ret;
-}
-
 package Git::SVN::Log;
 use strict;
 use warnings;
@@ -5290,7 +5546,7 @@ sub minimize_connections {
                        my $pfx = "svn-remote.$x->{old_repo_id}";
 
                        my $old_fetch = quotemeta("$x->{old_path}:".
-                                                 "refs/remotes/$x->{ref_id}");
+                                                 "$x->{ref_id}");
                        command_noisy(qw/config --unset/,
                                      "$pfx.fetch", '^'. $old_fetch . '$');
                        delete $r->{$x->{old_repo_id}}->
@@ -5359,7 +5615,7 @@ sub new {
        my ($class, $glob) = @_;
        my $re = $glob;
        $re =~ s!/+$!!g; # no need for trailing slashes
-       $re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!;
+       $re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!;
        my $temp = $re;
        my ($left, $right) = ($1, $3);
        $re = $2;
index 7ed0faddcd75e024ce18d8b80994d41a9207a7ca..a578c3a73203fbf1bf4abfb024b1e83c45f2b2ce 100755 (executable)
@@ -111,7 +111,8 @@ if test -z "$browser" ; then
        browser_candidates="w3m links lynx"
     fi
     # SECURITYSESSIONID indicates an OS X GUI login session
-    if test -n "$SECURITYSESSIONID"; then
+    if test -n "$SECURITYSESSIONID" \
+           -o "$TERM_PROGRAM" = "Apple_Terminal" ; then
        browser_candidates="open $browser_candidates"
     fi
     # /bin/start indicates MinGW
@@ -161,9 +162,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..9883009b5d9f29d4f53187947a6d8664da4d80f4 100644 (file)
--- a/git.c
+++ b/git.c
@@ -5,7 +5,10 @@
 #include "run-command.h"
 
 const char git_usage_string[] =
-       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
+       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
+       "           [-p|--paginate|--no-pager]\n"
+       "           [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
+       "           [--help] COMMAND [ARGS]";
 
 const char git_more_info_string[] =
        "See 'git help COMMAND' for more information on a specific command.";
@@ -188,10 +191,9 @@ static int handle_alias(int *argcp, const char ***argv)
                                  alias_command);
 
                new_argv = xrealloc(new_argv, sizeof(char *) *
-                                   (count + *argcp + 1));
+                                   (count + *argcp));
                /* insert after command name */
                memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
-               new_argv[count+*argcp] = NULL;
 
                *argv = new_argv;
                *argcp += count - 1;
@@ -200,7 +202,7 @@ static int handle_alias(int *argcp, const char ***argv)
        }
 
        if (subdir && chdir(subdir))
-               die("Cannot change to %s: %s", subdir, strerror(errno));
+               die_errno("Cannot change to '%s'", subdir);
 
        errno = saved_errno;
 
@@ -246,7 +248,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
        status = p->fn(argc, argv, prefix);
        if (status)
-               return status & 0xff;
+               return status;
 
        /* Somebody closed stdout? */
        if (fstat(fileno(stdout), &st))
@@ -257,11 +259,11 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
        /* Check for ENOSPC and EIO errors.. */
        if (fflush(stdout))
-               die("write failure on standard output: %s", strerror(errno));
+               die_errno("write failure on standard output");
        if (ferror(stdout))
                die("unknown write failure on standard output");
        if (fclose(stdout))
-               die("close failed on standard output: %s", strerror(errno));
+               die_errno("close failed on standard output");
        return 0;
 }
 
@@ -310,9 +312,6 @@ static void handle_internal_command(int argc, const char **argv)
                { "get-tar-commit-id", cmd_get_tar_commit_id },
                { "grep", cmd_grep, RUN_SETUP | USE_PAGER },
                { "help", cmd_help },
-#ifndef NO_CURL
-               { "http-fetch", cmd_http_fetch, RUN_SETUP },
-#endif
                { "init", cmd_init_db },
                { "init-db", cmd_init_db },
                { "log", cmd_log, RUN_SETUP | USE_PAGER },
@@ -327,6 +326,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 },
@@ -339,6 +339,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "receive-pack", cmd_receive_pack },
                { "reflog", cmd_reflog, RUN_SETUP },
                { "remote", cmd_remote, RUN_SETUP },
+               { "replace", cmd_replace, RUN_SETUP },
                { "repo-config", cmd_config },
                { "rerere", cmd_rerere, RUN_SETUP },
                { "reset", cmd_reset, RUN_SETUP },
@@ -358,6 +359,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
                { "update-index", cmd_update_index, RUN_SETUP },
                { "update-ref", cmd_update_ref, RUN_SETUP },
+               { "update-server-info", cmd_update_server_info, RUN_SETUP },
                { "upload-archive", cmd_upload_archive },
                { "verify-tag", cmd_verify_tag, RUN_SETUP },
                { "version", cmd_version },
@@ -416,13 +418,9 @@ static void execv_dashed_external(const char **argv)
         * if we fail because the command is not found, it is
         * OK to return. Otherwise, we just pass along the status code.
         */
-       status = run_command_v_opt(argv, 0);
-       if (status != -ERR_RUN_COMMAND_EXEC) {
-               if (IS_RUN_COMMAND_ERR(status))
-                       die("unable to run '%s'", argv[0]);
-               exit(-status);
-       }
-       errno = ENOENT; /* as if we called execvp */
+       status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
+       if (status >= 0 || errno != ENOENT)
+               exit(status);
 
        argv[0] = tmp;
 
index 4be0834f0bc1cebc2b341ef6f54c3c37f48eb832..ab224f7eae858912f1bc13b5dfba4181fe6cfdc6 100644 (file)
@@ -233,7 +233,7 @@ rm -rf $RPM_BUILD_ROOT
 * Tue Mar 27 2007 Eygene Ryabinkin <rea-git@codelabs.ru>
 - Added the git-p4 package: Perforce import stuff.
 
-* Mon Feb 13 2007 Nicolas Pitre <nico@cam.org>
+* Mon Feb 13 2007 Nicolas Pitre <nico@fluxnic.net>
 - Update core package description (Git isn't as stupid as it used to be)
 
 * Mon Feb 12 2007 Junio C Hamano <junkio@cox.net>
index 1a7887b2528140d63b84a72843c0d0ef64f56538..a0214b7004f141b7c918a5360d85f8f9e2db285c 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
@@ -287,7 +288,7 @@ proc parseviewrevs {view revs} {
            if {$sdm != 2} {
                lappend ret $id
            } else {
-               lset ret end [lindex $ret end]...$id
+               lset ret end $id...[lindex $ret end]
            }
            lappend pos $id
        }
@@ -1676,6 +1677,7 @@ proc readrefs {} {
     global tagids idtags headids idheads tagobjid
     global otherrefids idotherrefs mainhead mainheadid
     global selecthead selectheadid
+    global hideremotes
 
     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
        catch {unset $v}
@@ -1688,7 +1690,7 @@ proc readrefs {} {
        if {![string match "refs/*" $ref]} continue
        set name [string range $ref 5 end]
        if {[string match "remotes/*" $name]} {
-           if {![string match "*/HEAD" $name]} {
+           if {![string match "*/HEAD" $name] && !$hideremotes} {
                set headids($name) $id
                lappend idheads($id) $name
            }
@@ -2145,7 +2147,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
@@ -2519,10 +2521,12 @@ proc savestuff {w} {
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
     global autoselect extdifftool perfile_attrs markbgcolor
+    global hideremotes
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
     catch {
+       if {[file exists ~/.gitk-new]} {file delete -force ~/.gitk-new}
        set f [open "~/.gitk-new" w]
        if {$::tcl_platform(platform) eq {windows}} {
            file attributes "~/.gitk-new" -hidden true
@@ -2538,6 +2542,7 @@ proc savestuff {w} {
        puts $f [list set wrapcomment $wrapcomment]
        puts $f [list set autoselect $autoselect]
        puts $f [list set showneartags $showneartags]
+       puts $f [list set hideremotes $hideremotes]
        puts $f [list set showlocalchanges $showlocalchanges]
        puts $f [list set datetimeformat $datetimeformat]
        puts $f [list set limitdiffs $limitdiffs]
@@ -3163,6 +3168,28 @@ proc flist_hl {only} {
     set gdttype [mc "touching paths:"]
 }
 
+proc gitknewtmpdir {} {
+    global diffnum gitktmpdir gitdir
+
+    if {![info exists gitktmpdir]} {
+       set gitktmpdir [file join [file dirname $gitdir] \
+                           [format ".gitk-tmp.%s" [pid]]]
+       if {[catch {file mkdir $gitktmpdir} err]} {
+           error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
+           unset gitktmpdir
+           return {}
+       }
+       set diffnum 0
+    }
+    incr diffnum
+    set diffdir [file join $gitktmpdir $diffnum]
+    if {[catch {file mkdir $diffdir} err]} {
+       error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
+       return {}
+    }
+    return $diffdir
+}
+
 proc save_file_from_commit {filename output what} {
     global nullfile
 
@@ -3197,11 +3224,10 @@ proc external_diff_get_one_file {diffid filename diffdir} {
 }
 
 proc external_diff {} {
-    global gitktmpdir nullid nullid2
+    global nullid nullid2
     global flist_menu_file
     global diffids
-    global diffnum
-    global gitdir extdifftool
+    global extdifftool
 
     if {[llength $diffids] == 1} {
         # no reference commit given
@@ -3223,22 +3249,8 @@ proc external_diff {} {
     }
 
     # make sure that several diffs wont collide
-    if {![info exists gitktmpdir]} {
-       set gitktmpdir [file join [file dirname $gitdir] \
-                           [format ".gitk-tmp.%s" [pid]]]
-       if {[catch {file mkdir $gitktmpdir} err]} {
-           error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
-           unset gitktmpdir
-           return
-       }
-       set diffnum 0
-    }
-    incr diffnum
-    set diffdir [file join $gitktmpdir $diffnum]
-    if {[catch {file mkdir $diffdir} err]} {
-       error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
-       return
-    }
+    set diffdir [gitknewtmpdir]
+    if {$diffdir eq {}} return
 
     # gather files to diff
     set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
@@ -3671,17 +3683,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 +3724,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 +3744,7 @@ proc encode_view_opts {n} {
            }
        }
     }
+    set rargs [concat $rargs [shellsplit $newviewopts($n,refs)]]
     return [concat $rargs [shellsplit $newviewopts($n,args)]]
 }
 
@@ -3714,14 +3752,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 +3781,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 +3800,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 +3842,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 +3870,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 +3904,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 +7319,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 +7337,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"
     }
@@ -7333,7 +7408,7 @@ proc getblobdiffline {bdf ids} {
     $ctext conf -state normal
     while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
        if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
-           close $bdf
+           catch {close $bdf}
            return 0
        }
        if {![string compare -length 5 "diff " $line]} {
@@ -7485,7 +7560,7 @@ proc getblobdiffline {bdf ids} {
     }
     $ctext conf -state disabled
     if {[eof $bdf]} {
-       close $bdf
+       catch {close $bdf}
        return 0
     }
     return [expr {$nr >= 1000? 2: 1}]
@@ -7842,6 +7917,11 @@ proc gotocommit {} {
                }
                set id [lindex $matches 0]
            }
+       } else {
+           if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
+               error_popup [mc "Revision %s is not known" $sha1string]
+               return
+           }
        }
     }
     if {[commitinview $id $curview]} {
@@ -7851,7 +7931,7 @@ proc gotocommit {} {
     if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
        set msg [mc "SHA1 id %s is not known" $sha1string]
     } else {
-       set msg [mc "Tag/Head %s is not known" $sha1string]
+       set msg [mc "Revision %s is not in the current view" $sha1string]
     }
     error_popup $msg
 }
@@ -8201,8 +8281,11 @@ proc do_cmp_commits {a b} {
                appendshortlink $a [mc "Commit "] "  $heada\n"
                appendshortlink $b [mc " differs from\n       "] \
                    "  $headb\n"
-               $ctext insert end [mc "- stopping\n"]
-               break
+               $ctext insert end [mc "Diff of commits:\n\n"]
+               $ctext conf -state disabled
+               update
+               diffcommits $a $b
+               return
            }
        }
        if {$skipa} {
@@ -8228,6 +8311,31 @@ proc do_cmp_commits {a b} {
     $ctext conf -state disabled
 }
 
+proc diffcommits {a b} {
+    global diffcontext diffids blobdifffd diffinhdr
+
+    set tmpdir [gitknewtmpdir]
+    set fna [file join $tmpdir "commit-[string range $a 0 7]"]
+    set fnb [file join $tmpdir "commit-[string range $b 0 7]"]
+    if {[catch {
+       exec git diff-tree -p --pretty $a >$fna
+       exec git diff-tree -p --pretty $b >$fnb
+    } err]} {
+       error_popup [mc "Error writing commit to file: %s" $err]
+       return
+    }
+    if {[catch {
+       set fd [open "| diff -U$diffcontext $fna $fnb" r]
+    } err]} {
+       error_popup [mc "Error diffing commits: %s" $err]
+       return
+    }
+    set diffids [list commits $a $b]
+    set blobdifffd($diffids) $fd
+    set diffinhdr 0
+    filerun $fd [list getblobdiffline $fd $diffids]
+}
+
 proc diffvssel {dirn} {
     global rowmenuid selectedline
 
@@ -10319,6 +10427,7 @@ proc doprefs {} {
     global oldprefs prefstop showneartags showlocalchanges
     global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
     global tabstop limitdiffs autoselect extdifftool perfile_attrs
+    global hideremotes
 
     set top .gitkprefs
     set prefstop $top
@@ -10327,7 +10436,7 @@ proc doprefs {} {
        return
     }
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-                  limitdiffs tabstop perfile_attrs} {
+                  limitdiffs tabstop perfile_attrs hideremotes} {
        set oldprefs($v) [set $v]
     }
     toplevel $top
@@ -10359,6 +10468,9 @@ proc doprefs {} {
     checkbutton $top.ntag -text [mc "Display nearby tags"] \
        -font optionfont -variable showneartags
     grid x $top.ntag -sticky w
+    checkbutton $top.hideremotes -text [mc "Hide remote refs"] \
+       -font optionfont -variable hideremotes
+    grid x $top.hideremotes -sticky w
     checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \
        -font optionfont -variable limitdiffs
     grid x $top.ldiff -sticky w
@@ -10483,7 +10595,7 @@ proc prefscan {} {
     global oldprefs prefstop
 
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-                  limitdiffs tabstop perfile_attrs} {
+                  limitdiffs tabstop perfile_attrs hideremotes} {
        global $v
        set $v $oldprefs($v)
     }
@@ -10497,6 +10609,7 @@ proc prefsok {} {
     global oldprefs prefstop showneartags showlocalchanges
     global fontpref mainfont textfont uifont
     global limitdiffs treediffs perfile_attrs
+    global hideremotes
 
     catch {destroy $prefstop}
     unset prefstop
@@ -10542,6 +10655,9 @@ proc prefsok {} {
          $limitdiffs != $oldprefs(limitdiffs)} {
        reselectline
     }
+    if {$hideremotes != $oldprefs(hideremotes)} {
+       rereadrefs
+    }
 }
 
 proc formatdate {d} {
@@ -10837,7 +10953,7 @@ proc gitattr {path attr default} {
     } else {
        set r "unspecified"
        if {![catch {set line [exec git check-attr $attr -- $path]}]} {
-           regexp "(.*): encoding: (.*)" $line m f r
+           regexp "(.*): $attr: (.*)" $line m f r
        }
        set path_attr_cache($attr,$path) $r
     }
@@ -10865,7 +10981,7 @@ proc cache_gitattr {attr pathlist} {
        set newlist [lrange $newlist $lim end]
        if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
            foreach row [split $rlist "\n"] {
-               if {[regexp "(.*): encoding: (.*)" $row m path value]} {
+               if {[regexp "(.*): $attr: (.*)" $row m path value]} {
                    if {[string index $path 0] eq "\""} {
                        set path [encoding convertfrom [lindex $path 0]]
                    }
@@ -10947,6 +11063,7 @@ set mingaplen 100
 set cmitmode "patch"
 set wrapcomment "none"
 set showneartags 1
+set hideremotes 0
 set maxrefs 20
 set maxlinelen 200
 set showlocalchanges 1
@@ -11090,6 +11207,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 947b53f6b0d76bc169e4aee2e8a030963fdc131f..624eb2281e0bbd7c6dc55e0345bfd9acf6e46eb4 100644 (file)
@@ -1,32 +1,40 @@
 # Swedish translation for gitk
-# Copyright (C) 2005-2008 Paul Mackerras
+# Copyright (C) 2005-2009 Paul Mackerras
 # This file is distributed under the same license as the gitk package.
 #
-# Peter Karlsson <peter@softwolves.pp.se>, 2008.
+# Peter Krefting <peter@softwolves.pp.se>, 2008-2009.
 # Mikael Magnusson <mikachu@gmail.com>, 2008.
 msgid ""
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-10-18 22:03+1100\n"
-"PO-Revision-Date: 2008-08-03 19:03+0200\n"
-"Last-Translator: Mikael Magnusson <mikachu@gmail.com>\n"
-"Language-Team: Swedish <sv@li.org>\n"
+"POT-Creation-Date: 2009-08-13 13:38+0100\n"
+"PO-Revision-Date: 2009-08-13 13:40+0100\n"
+"Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
+"Content-Transfer-Encoding: 8bit"
 
 #: gitk:113
 msgid "Couldn't get list of unmerged files:"
-msgstr "Kunde inta hämta lista över ej sammanslagna filer:"
+msgstr "Kunde inte hämta lista över ej sammanslagna filer:"
 
-#: gitk:340
+#: gitk:269
+msgid "Error parsing revisions:"
+msgstr "Fel vid tolkning av revisioner:"
+
+#: gitk:324
+msgid "Error executing --argscmd command:"
+msgstr "Fel vid körning av --argscmd-kommando:"
+
+#: gitk:337
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
 "Inga filer valdes: --merge angavs men det finns inga filer som inte har "
 "slagits samman."
 
-#: gitk:343
+#: gitk:340
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -34,261 +42,290 @@ msgstr ""
 "Inga filer valdes: --merge angavs men det finns inga filer inom "
 "filbegränsningen."
 
-#: gitk:365 gitk:503
+#: gitk:362 gitk:509
 msgid "Error executing git log:"
 msgstr "Fel vid körning av git log:"
 
-#: gitk:378
+#: gitk:380 gitk:525
 msgid "Reading"
 msgstr "Läser"
 
-#: gitk:438 gitk:3462
+#: gitk:440 gitk:4123
 msgid "Reading commits..."
 msgstr "Läser incheckningar..."
 
-#: gitk:441 gitk:1528 gitk:3465
+#: gitk:443 gitk:1561 gitk:4126
 msgid "No commits selected"
 msgstr "Inga incheckningar markerade"
 
-#: gitk:1399
+#: gitk:1437
 msgid "Can't parse git log output:"
 msgstr "Kan inte tolka utdata från git log:"
 
-#: gitk:1605
+#: gitk:1657
 msgid "No commit information available"
 msgstr "Ingen incheckningsinformation är tillgänglig"
 
-#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
+#: gitk:1793 gitk:1817 gitk:3916 gitk:8786 gitk:10322 gitk:10498
 msgid "OK"
 msgstr "OK"
 
-#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
-#: gitk:9294 gitk:9467
+#: gitk:1819 gitk:3918 gitk:8383 gitk:8457 gitk:8567 gitk:8616 gitk:8788
+#: gitk:10323 gitk:10499
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: gitk:1811
+#: gitk:1919
 msgid "Update"
 msgstr "Uppdatera"
 
-#: gitk:1812
+#: gitk:1920
 msgid "Reload"
 msgstr "Ladda om"
 
-#: gitk:1813
+#: gitk:1921
 msgid "Reread references"
 msgstr "Läs om referenser"
 
-#: gitk:1814
+#: gitk:1922
 msgid "List references"
 msgstr "Visa referenser"
 
-#: gitk:1815
+#: gitk:1924
+msgid "Start git gui"
+msgstr "Starta git gui"
+
+#: gitk:1926
 msgid "Quit"
 msgstr "Avsluta"
 
-#: gitk:1810
+#: gitk:1918
 msgid "File"
 msgstr "Arkiv"
 
-#: gitk:1818
+#: gitk:1930
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: gitk:1817
+#: gitk:1929
 msgid "Edit"
 msgstr "Redigera"
 
-#: gitk:1821
+#: gitk:1934
 msgid "New view..."
 msgstr "Ny vy..."
 
-#: gitk:1822
+#: gitk:1935
 msgid "Edit view..."
 msgstr "Ändra vy..."
 
-#: gitk:1823
+#: gitk:1936
 msgid "Delete view"
 msgstr "Ta bort vy"
 
-#: gitk:1825
+#: gitk:1938
 msgid "All files"
 msgstr "Alla filer"
 
-#: gitk:1820 gitk:3196
+#: gitk:1933 gitk:3670
 msgid "View"
 msgstr "Visa"
 
-#: gitk:1828 gitk:2487
+#: gitk:1943 gitk:1953 gitk:2654
 msgid "About gitk"
 msgstr "Om gitk"
 
-#: gitk:1829
+#: gitk:1944 gitk:1958
 msgid "Key bindings"
 msgstr "Tangentbordsbindningar"
 
-#: gitk:1827
+#: gitk:1942 gitk:1957
 msgid "Help"
 msgstr "Hjälp"
 
-#: gitk:1887
+#: gitk:2018
 msgid "SHA1 ID: "
 msgstr "SHA1-id: "
 
-#: gitk:1918
+#: gitk:2049
 msgid "Row"
 msgstr "Rad"
 
-#: gitk:1949
+#: gitk:2080
 msgid "Find"
 msgstr "Sök"
 
-#: gitk:1950
+#: gitk:2081
 msgid "next"
 msgstr "nästa"
 
-#: gitk:1951
+#: gitk:2082
 msgid "prev"
 msgstr "föreg"
 
-#: gitk:1952
+#: gitk:2083
 msgid "commit"
 msgstr "incheckning"
 
-#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
+#: gitk:2086 gitk:2088 gitk:4284 gitk:4307 gitk:4331 gitk:6272 gitk:6344
+#: gitk:6428
 msgid "containing:"
 msgstr "som innehåller:"
 
-#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
+#: gitk:2089 gitk:3162 gitk:3167 gitk:4359
 msgid "touching paths:"
 msgstr "som rör sökväg:"
 
-#: gitk:1959 gitk:3697
+#: gitk:2090 gitk:4364
 msgid "adding/removing string:"
 msgstr "som lägger/till tar bort sträng:"
 
-#: gitk:1968 gitk:1970
+#: gitk:2099 gitk:2101
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:1970 gitk:3773 gitk:5518
+#: gitk:2101 gitk:4439 gitk:6240
 msgid "IgnCase"
 msgstr "IgnVersaler"
 
-#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
+#: gitk:2101 gitk:4333 gitk:4437 gitk:6236
 msgid "Regexp"
 msgstr "Reg.uttr."
 
-#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
+#: gitk:2103 gitk:2104 gitk:4458 gitk:4488 gitk:4495 gitk:6364 gitk:6432
 msgid "All fields"
 msgstr "Alla fält"
 
-#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
+#: gitk:2104 gitk:4456 gitk:4488 gitk:6303
 msgid "Headline"
 msgstr "Rubrik"
 
-#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
+#: gitk:2105 gitk:4456 gitk:6303 gitk:6432 gitk:6866
 msgid "Comments"
 msgstr "Kommentarer"
 
-#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
-#: gitk:7300
+#: gitk:2105 gitk:4456 gitk:4460 gitk:4495 gitk:6303 gitk:6801 gitk:8063
+#: gitk:8078
 msgid "Author"
 msgstr "Författare"
 
-#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
+#: gitk:2105 gitk:4456 gitk:6303 gitk:6803
 msgid "Committer"
 msgstr "Incheckare"
 
-#: gitk:2003
+#: gitk:2134
 msgid "Search"
 msgstr "Sök"
 
-#: gitk:2010
+#: gitk:2141
 msgid "Diff"
 msgstr "Diff"
 
-#: gitk:2012
+#: gitk:2143
 msgid "Old version"
 msgstr "Gammal version"
 
-#: gitk:2014
+#: gitk:2145
 msgid "New version"
 msgstr "Ny version"
 
-#: gitk:2016
+#: gitk:2147
 msgid "Lines of context"
 msgstr "Rader sammanhang"
 
-#: gitk:2026
+#: gitk:2157
 msgid "Ignore space change"
 msgstr "Ignorera ändringar i blanksteg"
 
-#: gitk:2084
+#: gitk:2215
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:2086
+#: gitk:2217
 msgid "Tree"
 msgstr "Träd"
 
-#: gitk:2213 gitk:2226
+#: gitk:2361 gitk:2378
 msgid "Diff this -> selected"
 msgstr "Diff denna -> markerad"
 
-#: gitk:2214 gitk:2227
+#: gitk:2362 gitk:2379
 msgid "Diff selected -> this"
 msgstr "Diff markerad -> denna"
 
-#: gitk:2215 gitk:2228
+#: gitk:2363 gitk:2380
 msgid "Make patch"
 msgstr "Skapa patch"
 
-#: gitk:2216 gitk:7494
+#: gitk:2364 gitk:8441
 msgid "Create tag"
 msgstr "Skapa tagg"
 
-#: gitk:2217 gitk:7593
+#: gitk:2365 gitk:8547
 msgid "Write commit to file"
 msgstr "Skriv incheckning till fil"
 
-#: gitk:2218 gitk:7647
+#: gitk:2366 gitk:8604
 msgid "Create new branch"
 msgstr "Skapa ny gren"
 
-#: gitk:2219
+#: gitk:2367
 msgid "Cherry-pick this commit"
 msgstr "Plocka denna incheckning"
 
-#: gitk:2220
+#: gitk:2368
 msgid "Reset HEAD branch to here"
 msgstr "Återställ HEAD-grenen hit"
 
-#: gitk:2234
+#: gitk:2369
+msgid "Mark this commit"
+msgstr "Markera denna incheckning"
+
+#: gitk:2370
+msgid "Return to mark"
+msgstr "Återgå till markering"
+
+#: gitk:2371
+msgid "Find descendant of this and mark"
+msgstr "Hitta efterföljare till denna och markera"
+
+#: gitk:2372
+msgid "Compare with marked commit"
+msgstr "Jämför med markerad incheckning"
+
+#: gitk:2386
 msgid "Check out this branch"
 msgstr "Checka ut denna gren"
 
-#: gitk:2235
+#: gitk:2387
 msgid "Remove this branch"
 msgstr "Ta bort denna gren"
 
-#: gitk:2242
+#: gitk:2394
 msgid "Highlight this too"
 msgstr "Markera även detta"
 
-#: gitk:2243
+#: gitk:2395
 msgid "Highlight this only"
 msgstr "Markera bara detta"
 
-#: gitk:2244
+#: gitk:2396
 msgid "External diff"
 msgstr "Extern diff"
 
-#: gitk:2245
+#: gitk:2397
 msgid "Blame parent commit"
-msgstr ""
+msgstr "Klandra föräldraincheckning"
 
-#: gitk:2488
+#: gitk:2404
+msgid "Show origin of this line"
+msgstr "Visa ursprunget för den här raden"
+
+#: gitk:2405
+msgid "Run git gui blame on this line"
+msgstr "Kör git gui blame på den här raden"
+
+#: gitk:2656
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -304,427 +341,672 @@ msgstr ""
 "\n"
 "Använd och vidareförmedla enligt villkoren i GNU General Public License"
 
-#: gitk:2496 gitk:2557 gitk:7943
+#: gitk:2664 gitk:2726 gitk:8969
 msgid "Close"
 msgstr "Stäng"
 
-#: gitk:2515
+#: gitk:2683
 msgid "Gitk key bindings"
 msgstr "Tangentbordsbindningar för Gitk"
 
-#: gitk:2517
+#: gitk:2686
 msgid "Gitk key bindings:"
 msgstr "Tangentbordsbindningar för Gitk:"
 
-#: gitk:2519
+#: gitk:2688
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tAvsluta"
 
-#: gitk:2520
+#: gitk:2689
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tGå till första incheckning"
 
-#: gitk:2521
+#: gitk:2690
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tGå till sista incheckning"
 
-#: gitk:2522
+#: gitk:2691
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Upp>, p, i\tGå en incheckning upp"
 
-#: gitk:2523
+#: gitk:2692
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Ned>, n, k\tGå en incheckning ned"
 
-#: gitk:2524
+#: gitk:2693
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Vänster>, z, j\tGå bakåt i historiken"
 
-#: gitk:2525
+#: gitk:2694
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Höger>, x, l\tGå framåt i historiken"
 
-#: gitk:2526
+#: gitk:2695
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tGå upp en sida i incheckningslistan"
 
-#: gitk:2527
+#: gitk:2696
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tGå ned en sida i incheckningslistan"
 
-#: gitk:2528
+#: gitk:2697
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tRulla till början av incheckningslistan"
 
-#: gitk:2529
+#: gitk:2698
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tRulla till slutet av incheckningslistan"
 
-#: gitk:2530
+#: gitk:2699
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg"
 
-#: gitk:2531
+#: gitk:2700
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg"
 
-#: gitk:2532
+#: gitk:2701
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida"
 
-#: gitk:2533
+#: gitk:2702
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida"
 
-#: gitk:2534
+#: gitk:2703
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)"
 
-#: gitk:2535
+#: gitk:2704
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)"
 
-#: gitk:2536
+#: gitk:2705
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tRulla diffvisningen upp en sida"
 
-#: gitk:2537
+#: gitk:2706
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Baksteg>\tRulla diffvisningen upp en sida"
 
-#: gitk:2538
+#: gitk:2707
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Blanksteg>\tRulla diffvisningen ned en sida"
 
-#: gitk:2539
+#: gitk:2708
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tRulla diffvisningen upp 18 rader"
 
-#: gitk:2540
+#: gitk:2709
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tRulla diffvisningen ned 18 rader"
 
-#: gitk:2541
+#: gitk:2710
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSök"
 
-#: gitk:2542
+#: gitk:2711
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tGå till nästa sökträff"
 
-#: gitk:2543
+#: gitk:2712
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\t\tGå till nästa sökträff"
 
-#: gitk:2544
-msgid "/\t\tMove to next find hit, or redo find"
-msgstr "/\t\tGå till nästa sökträff, eller sök på nytt"
+#: gitk:2713
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tFokusera sökrutan"
 
-#: gitk:2545
+#: gitk:2714
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tGå till föregående sökträff"
 
-#: gitk:2546
+#: gitk:2715
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tRulla diffvisningen till nästa fil"
 
-#: gitk:2547
+#: gitk:2716
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tGå till nästa sökträff i diffvisningen"
 
-#: gitk:2548
+#: gitk:2717
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tGå till föregående sökträff i diffvisningen"
 
-#: gitk:2549
+#: gitk:2718
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Num+>\tÖka teckenstorlek"
 
-#: gitk:2550
+#: gitk:2719
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tÖka teckenstorlek"
 
-#: gitk:2551
+#: gitk:2720
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Num->\tMinska teckenstorlek"
 
-#: gitk:2552
+#: gitk:2721
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tMinska teckenstorlek"
 
-#: gitk:2553
+#: gitk:2722
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tUppdatera"
 
-#: gitk:3200
+#: gitk:3177
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Fel vid hämtning av  \"%s\" från %s:"
+
+#: gitk:3234 gitk:3243
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Fel vid skapande av temporär katalog %s:"
+
+#: gitk:3255
+msgid "command failed:"
+msgstr "kommando misslyckades:"
+
+#: gitk:3401
+msgid "No such commit"
+msgstr "Incheckning saknas"
+
+#: gitk:3415
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: kommando misslyckades:"
+
+#: gitk:3446
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Kunde inte läsa sammanslagningshuvud: %s"
+
+#: gitk:3454
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Fel vid läsning av index: %s"
+
+#: gitk:3479
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Kunde inte starta git blame: %s"
+
+#: gitk:3482 gitk:6271
+msgid "Searching"
+msgstr "Söker"
+
+#: gitk:3514
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Fel vid körning av git blame: %s"
+
+#: gitk:3542
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Raden kommer från incheckningen %s, som inte finns i denna vy"
+
+#: gitk:3556
+msgid "External diff viewer failed:"
+msgstr "Externt diff-verktyg misslyckades:"
+
+#: gitk:3674
 msgid "Gitk view definition"
 msgstr "Definition av Gitk-vy"
 
-#: gitk:3225
-msgid "Name"
-msgstr "Namn"
-
-#: gitk:3228
+#: gitk:3678
 msgid "Remember this view"
 msgstr "Spara denna vy"
 
-#: gitk:3232
-msgid "Commits to include (arguments to git log):"
-msgstr "Incheckningar att ta med (argument till git log):"
+#: gitk:3679
+msgid "References (space separated list):"
+msgstr "Referenser (blankstegsavdelad lista):"
 
-#: gitk:3239
-msgid "Command to generate more commits to include:"
-msgstr "Kommando för att generera fler incheckningar att ta med:"
+#: gitk:3680
+msgid "Branches & tags:"
+msgstr "Grenar & taggar:"
+
+#: gitk:3681
+msgid "All refs"
+msgstr "Alla referenser"
+
+#: gitk:3682
+msgid "All (local) branches"
+msgstr "Alla (lokala) grenar"
+
+#: gitk:3683
+msgid "All tags"
+msgstr "Alla taggar"
+
+#: gitk:3684
+msgid "All remote-tracking branches"
+msgstr "Alla fjärrspårande grenar"
+
+#: gitk:3685
+msgid "Commit Info (regular expressions):"
+msgstr "Incheckningsinfo (reguljära uttryck):"
 
-#: gitk:3246
+#: gitk:3686
+msgid "Author:"
+msgstr "Författare:"
+
+#: gitk:3687
+msgid "Committer:"
+msgstr "Incheckare:"
+
+#: gitk:3688
+msgid "Commit Message:"
+msgstr "Incheckningsmeddelande:"
+
+#: gitk:3689
+msgid "Matches all Commit Info criteria"
+msgstr "Motsvarar alla kriterier för incheckningsinfo"
+
+#: gitk:3690
+msgid "Changes to Files:"
+msgstr "Ändringar av filer:"
+
+#: gitk:3691
+msgid "Fixed String"
+msgstr "Fast sträng"
+
+#: gitk:3692
+msgid "Regular Expression"
+msgstr "Reguljärt uttryck"
+
+#: gitk:3693
+msgid "Search string:"
+msgstr "Söksträng:"
+
+#: gitk:3694
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Incheckingsdatum (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+
+#: gitk:3695
+msgid "Since:"
+msgstr "Från:"
+
+#: gitk:3696
+msgid "Until:"
+msgstr "Till:"
+
+#: gitk:3697
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Begränsa och/eller hoppa över ett antal revisioner (positivt heltal):"
+
+#: gitk:3698
+msgid "Number to show:"
+msgstr "Antal att visa:"
+
+#: gitk:3699
+msgid "Number to skip:"
+msgstr "Antal att hoppa över:"
+
+#: gitk:3700
+msgid "Miscellaneous options:"
+msgstr "Diverse alternativ:"
+
+#: gitk:3701
+msgid "Strictly sort by date"
+msgstr "Strikt datumsortering"
+
+#: gitk:3702
+msgid "Mark branch sides"
+msgstr "Markera sidogrenar"
+
+#: gitk:3703
+msgid "Limit to first parent"
+msgstr "Begränsa till första förälder"
+
+#: gitk:3704
+msgid "Simple history"
+msgstr "Enkel historik"
+
+#: gitk:3705
+msgid "Additional arguments to git log:"
+msgstr "Ytterligare argument till git log:"
+
+#: gitk:3706
 msgid "Enter files and directories to include, one per line:"
 msgstr "Ange filer och kataloger att ta med, en per rad:"
 
-#: gitk:3293
+#: gitk:3707
+msgid "Command to generate more commits to include:"
+msgstr "Kommando för att generera fler incheckningar att ta med:"
+
+#: gitk:3829
+msgid "Gitk: edit view"
+msgstr "Gitk: redigera vy"
+
+#: gitk:3837
+msgid "-- criteria for selecting revisions"
+msgstr " - kriterier för val av revisioner"
+
+#: gitk:3842
+msgid "View Name:"
+msgstr "Namn på vy:"
+
+#: gitk:3917
+msgid "Apply (F5)"
+msgstr "Använd (F5)"
+
+#: gitk:3955
 msgid "Error in commit selection arguments:"
 msgstr "Fel i argument för val av incheckningar:"
 
-#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
+#: gitk:4008 gitk:4060 gitk:4508 gitk:4522 gitk:5783 gitk:11196 gitk:11197
 msgid "None"
 msgstr "Inget"
 
-#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
+#: gitk:4456 gitk:6303 gitk:8065 gitk:8080
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:3790 gitk:5580
+#: gitk:4456 gitk:6303
 msgid "CDate"
 msgstr "Skapat datum"
 
-#: gitk:3939 gitk:3944
+#: gitk:4605 gitk:4610
 msgid "Descendant"
 msgstr "Avkomling"
 
-#: gitk:3940
+#: gitk:4606
 msgid "Not descendant"
 msgstr "Inte avkomling"
 
-#: gitk:3947 gitk:3952
+#: gitk:4613 gitk:4618
 msgid "Ancestor"
 msgstr "Förfader"
 
-#: gitk:3948
+#: gitk:4614
 msgid "Not ancestor"
 msgstr "Inte förfader"
 
-#: gitk:4187
+#: gitk:4904
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokala ändringar sparade i indexet men inte incheckade"
 
-#: gitk:4220
+#: gitk:4940
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokala ändringar, ej sparade i indexet"
 
-#: gitk:5549
-msgid "Searching"
-msgstr "Söker"
+#: gitk:6621
+msgid "many"
+msgstr "många"
 
-#: gitk:6049
+#: gitk:6805
 msgid "Tags:"
 msgstr "Taggar:"
 
-#: gitk:6066 gitk:6072 gitk:7280
+#: gitk:6822 gitk:6828 gitk:8058
 msgid "Parent"
 msgstr "Förälder"
 
-#: gitk:6077
+#: gitk:6833
 msgid "Child"
 msgstr "Barn"
 
-#: gitk:6086
+#: gitk:6842
 msgid "Branch"
 msgstr "Gren"
 
-#: gitk:6089
+#: gitk:6845
 msgid "Follows"
 msgstr "Följer"
 
-#: gitk:6092
+#: gitk:6848
 msgid "Precedes"
 msgstr "Föregår"
 
-#: gitk:6378
-msgid "Error getting merge diffs:"
-msgstr "Fel vid hämtning av sammanslagningsdiff:"
+#: gitk:7346
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Fel vid hämtning av diff: %s"
 
-#: gitk:7113
+#: gitk:7886
 msgid "Goto:"
 msgstr "Gå till:"
 
-#: gitk:7115
+#: gitk:7888
 msgid "SHA1 ID:"
 msgstr "SHA1-id:"
 
-#: gitk:7134
+#: gitk:7907
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Förkortat SHA1-id %s är tvetydigt"
 
-#: gitk:7146
+#: gitk:7914
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "Revisionen %s är inte känd"
+
+#: gitk:7924
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA-id:t %s är inte känt"
 
-#: gitk:7148
+#: gitk:7926
 #, tcl-format
-msgid "Tag/Head %s is not known"
-msgstr "Tagg/huvud %s är okänt"
+msgid "Revision %s is not in the current view"
+msgstr "Revisionen %s finns inte i den nuvarande vyn"
 
-#: gitk:7290
+#: gitk:8068
 msgid "Children"
 msgstr "Barn"
 
-#: gitk:7347
+#: gitk:8125
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Återställ grenen %s hit"
 
-#: gitk:7349
+#: gitk:8127
 msgid "Detached head: can't reset"
 msgstr "Frånkopplad head: kan inte återställa"
 
-#: gitk:7381
+#: gitk:8236 gitk:8242
+msgid "Skipping merge commit "
+msgstr "Hoppar över sammanslagningsincheckning "
+
+#: gitk:8251 gitk:8256
+msgid "Error getting patch ID for "
+msgstr "Fel vid hämtning av patch-id för "
+
+#: gitk:8252 gitk:8257
+msgid " - stopping\n"
+msgstr " - stannar\n"
+
+#: gitk:8262 gitk:8265 gitk:8273 gitk:8283 gitk:8292
+msgid "Commit "
+msgstr "Incheckning "
+
+#: gitk:8266
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" är samma patch som\n"
+"       "
+
+#: gitk:8274
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" skiljer sig från\n"
+"       "
+
+#: gitk:8276
+msgid "- stopping\n"
+msgstr "- stannar\n"
+
+#: gitk:8284 gitk:8293
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " har %s barn - stannar\n"
+
+#: gitk:8324
 msgid "Top"
 msgstr "Topp"
 
-#: gitk:7382
+#: gitk:8325
 msgid "From"
 msgstr "Från"
 
-#: gitk:7387
+#: gitk:8330
 msgid "To"
 msgstr "Till"
 
-#: gitk:7410
+#: gitk:8354
 msgid "Generate patch"
 msgstr "Generera patch"
 
-#: gitk:7412
+#: gitk:8356
 msgid "From:"
 msgstr "Från:"
 
-#: gitk:7421
+#: gitk:8365
 msgid "To:"
 msgstr "Till:"
 
-#: gitk:7430
+#: gitk:8374
 msgid "Reverse"
 msgstr "Vänd"
 
-#: gitk:7432 gitk:7607
+#: gitk:8376 gitk:8561
 msgid "Output file:"
 msgstr "Utdatafil:"
 
-#: gitk:7438
+#: gitk:8382
 msgid "Generate"
 msgstr "Generera"
 
-#: gitk:7474
+#: gitk:8420
 msgid "Error creating patch:"
 msgstr "Fel vid generering av patch:"
 
-#: gitk:7496 gitk:7595 gitk:7649
+#: gitk:8443 gitk:8549 gitk:8606
 msgid "ID:"
 msgstr "Id:"
 
-#: gitk:7505
+#: gitk:8452
 msgid "Tag name:"
 msgstr "Taggnamn:"
 
-#: gitk:7509 gitk:7659
+#: gitk:8456 gitk:8615
 msgid "Create"
 msgstr "Skapa"
 
-#: gitk:7524
+#: gitk:8473
 msgid "No tag name specified"
 msgstr "Inget taggnamn angavs"
 
-#: gitk:7528
+#: gitk:8477
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Taggen \"%s\" finns redan"
 
-#: gitk:7534
+#: gitk:8483
 msgid "Error creating tag:"
 msgstr "Fel vid skapande av tagg:"
 
-#: gitk:7604
+#: gitk:8558
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:7612
+#: gitk:8566
 msgid "Write"
 msgstr "Skriv"
 
-#: gitk:7628
+#: gitk:8584
 msgid "Error writing commit:"
 msgstr "Fel vid skrivning av incheckning:"
 
-#: gitk:7654
+#: gitk:8611
 msgid "Name:"
 msgstr "Namn:"
 
-#: gitk:7674
+#: gitk:8634
 msgid "Please specify a name for the new branch"
 msgstr "Ange ett namn för den nya grenen"
 
-#: gitk:7703
+#: gitk:8639
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "Grenen \"%s\" finns redan. Skriva över?"
+
+#: gitk:8705
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Incheckningen %s finns redan på grenen %s -- skall den verkligen appliceras "
 "på nytt?"
 
-#: gitk:7708
+#: gitk:8710
 msgid "Cherry-picking"
 msgstr "Plockar"
 
-#: gitk:7720
+#: gitk:8719
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"Cherry-pick misslyckades på grund av lokala ändringar i filen \"%s\".\n"
+"Checka in, återställ eller spara undan (stash) dina ändringar och försök "
+"igen."
+
+#: gitk:8725
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Cherry-pick misslyckades på grund av en sammanslagningskonflikt.\n"
+"Vill du köra git citool för att lösa den?"
+
+#: gitk:8741
 msgid "No changes committed"
 msgstr "Inga ändringar incheckade"
 
-#: gitk:7745
+#: gitk:8767
 msgid "Confirm reset"
 msgstr "Bekräfta återställning"
 
-#: gitk:7747
+#: gitk:8769
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Återställa grenen %s till %s?"
 
-#: gitk:7751
+#: gitk:8773
 msgid "Reset type:"
 msgstr "Typ av återställning:"
 
-#: gitk:7755
+#: gitk:8777
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Mjuk: Rör inte utcheckning och index"
 
-#: gitk:7758
+#: gitk:8780
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Blandad: Rör inte utcheckning, återställ index"
 
-#: gitk:7761
+#: gitk:8783
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -732,19 +1014,19 @@ msgstr ""
 "Hård: Återställ utcheckning och index\n"
 "(förkastar ALLA lokala ändringar)"
 
-#: gitk:7777
+#: gitk:8800
 msgid "Resetting"
 msgstr "Återställer"
 
-#: gitk:7834
+#: gitk:8857
 msgid "Checking out"
 msgstr "Checkar ut"
 
-#: gitk:7885
+#: gitk:8910
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Kan inte ta bort den just nu utcheckade grenen"
 
-#: gitk:7891
+#: gitk:8916
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -753,16 +1035,16 @@ msgstr ""
 "Incheckningarna på grenen %s existerar inte på någon annan gren.\n"
 "Vill du verkligen ta bort grenen %s?"
 
-#: gitk:7922
+#: gitk:8947
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Taggar och huvuden: %s"
 
-#: gitk:7936
+#: gitk:8962
 msgid "Filter"
 msgstr "Filter"
 
-#: gitk:8230
+#: gitk:9257
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -770,129 +1052,161 @@ msgstr ""
 "Fel vid läsning av information om incheckningstopologi; information om "
 "grenar och föregående/senare taggar kommer inte vara komplett."
 
-#: gitk:9216
+#: gitk:10243
 msgid "Tag"
 msgstr "Tagg"
 
-#: gitk:9216
+#: gitk:10243
 msgid "Id"
 msgstr "Id"
 
-#: gitk:9262
+#: gitk:10291
 msgid "Gitk font chooser"
 msgstr "Teckensnittsväljare för Gitk"
 
-#: gitk:9279
+#: gitk:10308
 msgid "B"
 msgstr "F"
 
-#: gitk:9282
+#: gitk:10311
 msgid "I"
 msgstr "K"
 
-#: gitk:9375
+#: gitk:10407
 msgid "Gitk preferences"
 msgstr "Inställningar för Gitk"
 
-#: gitk:9376
+#: gitk:10409
 msgid "Commit list display options"
 msgstr "Alternativ för incheckningslistvy"
 
-#: gitk:9379
+#: gitk:10412
 msgid "Maximum graph width (lines)"
 msgstr "Maximal grafbredd (rader)"
 
-#: gitk:9383
+#: gitk:10416
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximal grafbredd (% av ruta)"
 
-#: gitk:9388
+#: gitk:10420
 msgid "Show local changes"
 msgstr "Visa lokala ändringar"
 
-#: gitk:9393
+#: gitk:10423
 msgid "Auto-select SHA1"
 msgstr "Välj SHA1 automatiskt"
 
-#: gitk:9398
+#: gitk:10427
 msgid "Diff display options"
 msgstr "Alternativ för diffvy"
 
-#: gitk:9400
+#: gitk:10429
 msgid "Tab spacing"
 msgstr "Blanksteg för tabulatortecken"
 
-#: gitk:9404
+#: gitk:10432
 msgid "Display nearby tags"
 msgstr "Visa närliggande taggar"
 
-#: gitk:9409
+#: gitk:10435
+msgid "Hide remote refs"
+msgstr "Dölj fjärr-referenser"
+
+#: gitk:10438
 msgid "Limit diffs to listed paths"
 msgstr "Begränsa diff till listade sökvägar"
 
-#: gitk:9414
+#: gitk:10441
 msgid "Support per-file encodings"
-msgstr ""
+msgstr "Stöd för filspecifika teckenkodningar"
 
-#: gitk:9421
+#: gitk:10447 gitk:10512
 msgid "External diff tool"
 msgstr "Externt diff-verktyg"
 
-#: gitk:9423
+#: gitk:10449
 msgid "Choose..."
 msgstr "Välj..."
 
-#: gitk:9428
+#: gitk:10454
 msgid "Colors: press to choose"
 msgstr "Färger: tryck för att välja"
 
-#: gitk:9431
+#: gitk:10457
 msgid "Background"
 msgstr "Bakgrund"
 
-#: gitk:9435
+#: gitk:10458 gitk:10488
+msgid "background"
+msgstr "bakgrund"
+
+#: gitk:10461
 msgid "Foreground"
 msgstr "Förgrund"
 
-#: gitk:9439
+#: gitk:10462
+msgid "foreground"
+msgstr "förgrund"
+
+#: gitk:10465
 msgid "Diff: old lines"
 msgstr "Diff: gamla rader"
 
-#: gitk:9444
+#: gitk:10466
+msgid "diff old lines"
+msgstr "diff gamla rader"
+
+#: gitk:10470
 msgid "Diff: new lines"
 msgstr "Diff: nya rader"
 
-#: gitk:9449
+#: gitk:10471
+msgid "diff new lines"
+msgstr "diff nya rader"
+
+#: gitk:10475
 msgid "Diff: hunk header"
 msgstr "Diff: delhuvud"
 
-#: gitk:9455
+#: gitk:10477
+msgid "diff hunk header"
+msgstr "diff delhuvud"
+
+#: gitk:10481
+msgid "Marked line bg"
+msgstr "Markerad rad bakgrund"
+
+#: gitk:10483
+msgid "marked line background"
+msgstr "markerad rad bakgrund"
+
+#: gitk:10487
 msgid "Select bg"
 msgstr "Markerad bakgrund"
 
-#: gitk:9459
+#: gitk:10491
 msgid "Fonts: press to choose"
 msgstr "Teckensnitt: tryck för att välja"
 
-#: gitk:9461
+#: gitk:10493
 msgid "Main font"
 msgstr "Huvudteckensnitt"
 
-#: gitk:9462
+#: gitk:10494
 msgid "Diff display font"
 msgstr "Teckensnitt för diffvisning"
 
-#: gitk:9463
+#: gitk:10495
 msgid "User interface font"
 msgstr "Teckensnitt för användargränssnitt"
 
-#: gitk:9488
+#: gitk:10522
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: välj färg för %s"
 
-#: gitk:9934
+#: gitk:10973
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -900,24 +1214,33 @@ msgstr ""
 "Gitk kan tyvärr inte köra med denna version av Tcl/Tk.\n"
 " Gitk kräver åtminstone Tcl/Tk 8.4."
 
-#: gitk:10047
+#: gitk:11101
 msgid "Cannot find a git repository here."
 msgstr "Hittar inget gitk-arkiv här."
 
-#: gitk:10051
+#: gitk:11105
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Hittar inte git-katalogen \"%s\"."
 
-#: gitk:10098
+#: gitk:11152
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Tvetydigt argument \"%s\": både revision och filnamn"
 
-#: gitk:10110
+#: gitk:11164
 msgid "Bad arguments to gitk:"
 msgstr "Felaktiga argument till gitk:"
 
-#: gitk:10170
+#: gitk:11249
 msgid "Command line"
 msgstr "Kommandorad"
+
+#~ msgid "Tag/Head %s is not known"
+#~ msgstr "Tagg/huvud %s är okänt"
+
+#~ msgid "/\t\tMove to next find hit, or redo find"
+#~ msgstr "/\t\tGå till nästa sökträff, eller sök på nytt"
+
+#~ msgid "Name"
+#~ msgstr "Namn"
index 18c9ce35e8fc6566663ad76dd04bd1aa70035c25..b76a0cffff783ba580294560f0ee53131776136b 100644 (file)
@@ -123,6 +123,15 @@ GITWEB_CONFIG file:
        $feature{'snapshot'}{'default'} = ['zip', 'tgz'];
        $feature{'snapshot'}{'override'} = 1;
 
+If you allow overriding for the snapshot feature, you can specify which
+snapshot formats are globally disabled. You can also add any command line
+options you want (such as setting the compression level). For instance,
+you can disable Zip compressed snapshots and set GZip to run at level 6 by
+adding the following lines to your $GITWEB_CONFIG:
+
+       $known_snapshot_formats{'zip'}{'disabled'} = 1;
+       $known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6'];
+
 
 Gitweb repositories
 -------------------
index ccda890c0ef1b2d1fb1c451d3f9a10d97817c8f6..66c6a9391d3494ae8928e88501d8fa0d36dffa42 100644 (file)
@@ -165,6 +165,12 @@ not include variables usually directly set during build):
    Full URL and absolute URL of gitweb script;
    in earlier versions of gitweb you might have need to set those
    variables, now there should be no need to do it.
+ * $base_url
+   Base URL for relative URLs in pages generated by gitweb,
+   (e.g. $logo, $favicon, @stylesheets if they are relative URLs),
+   needed and used only for URLs with nonempty PATH_INFO via
+   <base href="$base_url>.  Usually gitweb sets its value correctly,
+   and there is no need to set this variable, e.g. to $my_uri or "/".
  * $home_link
    Target of the home link on top of all pages (the first part of view
    "breadcrumbs").  By default set to absolute URI of a page ($my_uri).
@@ -377,7 +383,7 @@ named without a .git extension (e.g. /pub/git/project instead of
 
        DocumentRoot /var/www/gitweb
 
-       AliasMatch ^(/.*?)(\.git)(/.*)? /pub/git$1$3
+       AliasMatch ^(/.*?)(\.git)(/.*)?$ /pub/git$1$3
        <Directory /var/www/gitweb>
                Options ExecCGI
                AddHandler cgi-script cgi
@@ -402,6 +408,14 @@ http://git.example.com/project
 
 will provide human-friendly gitweb access.
 
+This solution is not 100% bulletproof, in the sense that if some project
+has a named ref (branch, tag) starting with 'git/', then paths such as
+
+http://git.example.com/project/command/abranch..git/abranch
+
+will fail with a 404 error.
+
+
 
 Originally written by:
   Kay Sievers <kay.sievers@vrfy.org>
index de637c0608090162a6ce6b51d5f9bfe512cf8bcf..aae35a70e70351fe6dcb3e905e2e388cf0cb0ac3 100644 (file)
Binary files a/gitweb/git-favicon.png and b/gitweb/git-favicon.png differ
index 16ae8d5382de5ffe63b54139245143513a87446e..f4ede2e944868b9a08401dafeb2b944c7166fd0a 100644 (file)
Binary files a/gitweb/git-logo.png and b/gitweb/git-logo.png differ
index a01eac814e51edd2115474f4d48a8d7fafa8c0e6..d60bfc1f646ddfa019ddbfbab6d0c3198c34a6b9 100644 (file)
@@ -28,6 +28,10 @@ img.logo {
        border-width: 0px;
 }
 
+img.avatar {
+       vertical-align: middle;
+}
+
 div.page_header {
        height: 25px;
        padding: 8px;
@@ -132,11 +136,14 @@ div.list_head {
        font-style: italic;
 }
 
+.author_date, .author {
+       font-style: italic;
+}
+
 div.author_date {
        padding: 8px;
        border: solid #d9d8d1;
        border-width: 0px 0px 1px 0px;
-       font-style: italic;
 }
 
 a.list {
@@ -219,22 +226,30 @@ th {
        text-align: left;
 }
 
-tr.light:hover {
-       background-color: #edece6;
-}
-
-tr.dark {
-       background-color: #f6f6f0;
+/* do not change row style on hover for 'blame' view */
+tr.light,
+table.blame .light:hover {
+       background-color: #ffffff;
 }
 
-tr.dark2 {
+tr.dark,
+table.blame .dark:hover {
        background-color: #f6f6f0;
 }
 
+/* currently both use the same, but it can change */
+tr.light:hover,
 tr.dark:hover {
        background-color: #edece6;
 }
 
+/* boundary commits in 'blame' view */
+/* and commits without "previous" */
+tr.boundary td.sha1,
+tr.no-previous td.linenr {
+       font-weight: bold;
+}
+
 td {
        padding: 2px 5px;
        font-size: 100%;
@@ -255,7 +270,7 @@ td.sha1 {
        font-family: monospace;
 }
 
-td.error {
+.error {
        color: red;
        background-color: yellow;
 }
@@ -326,6 +341,12 @@ td.mode {
        font-family: monospace;
 }
 
+/* format of (optional) objects size in 'tree' view */
+td.size {
+       font-family: monospace;
+       text-align: right;
+}
+
 /* styling of diffs (patchsets): commitdiff and blobdiff views */
 div.diff.header,
 div.diff.extended_header {
index 3f99361ed03b8d5202cb17e894c65678364fc1ed..d6eb7291d66ffc8a6dc6ecc331d44f3652c5a98f 100755 (executable)
@@ -94,7 +94,7 @@ our $favicon = "++GITWEB_FAVICON++";
 # URI and label (title) of GIT logo link
 #our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
 #our $logo_label = "git documentation";
-our $logo_url = "http://git.or.cz/";
+our $logo_url = "http://git-scm.com/";
 our $logo_label = "git homepage";
 
 # source of projects list
@@ -160,7 +160,8 @@ our %known_snapshot_formats = (
        #       'suffix' => filename suffix,
        #       'format' => --format for git-archive,
        #       'compressor' => [compressor command and arguments]
-       #                       (array reference, optional)}
+       #                       (array reference, optional)
+       #       'disabled' => boolean (optional)}
        #
        'tgz' => {
                'display' => 'tar.gz',
@@ -176,6 +177,14 @@ our %known_snapshot_formats = (
                'format' => 'tar',
                'compressor' => ['bzip2']},
 
+       'txz' => {
+               'display' => 'tar.xz',
+               'type' => 'application/x-xz',
+               'suffix' => '.tar.xz',
+               'format' => 'tar',
+               'compressor' => ['xz'],
+               'disabled' => 1},
+
        'zip' => {
                'display' => 'zip',
                'type' => 'application/x-zip',
@@ -188,6 +197,7 @@ our %known_snapshot_formats = (
 our %known_snapshot_format_aliases = (
        'gzip'  => 'tgz',
        'bzip2' => 'tbz2',
+       'xz'    => 'txz',
 
        # backward compatibility: legacy gitweb config support
        'x-gzip' => undef, 'gz' => undef,
@@ -195,6 +205,14 @@ our %known_snapshot_format_aliases = (
        'x-zip' => undef, '' => undef,
 );
 
+# Pixel sizes for icons and avatars. If the default font sizes or lineheights
+# are changed, it may be appropriate to change these values too via
+# $GITWEB_CONFIG.
+our %avatar_size = (
+       'default' => 16,
+       'double'  => 32
+);
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -279,6 +297,19 @@ our %feature = (
                'override' => 0,
                'default' => [1]},
 
+       # Enable showing size of blobs in a 'tree' view, in a separate
+       # column, similar to what 'ls -l' does.  This cost a bit of IO.
+
+       # To disable system wide have in $GITWEB_CONFIG
+       # $feature{'show-sizes'}{'default'} = [0];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'show-sizes'}{'override'} = 1;
+       # and in project config gitweb.showsizes = 0|1;
+       'show-sizes' => {
+               'sub' => sub { feature_bool('showsizes', @_) },
+               'override' => 0,
+               'default' => [1]},
+
        # Make gitweb use an alternative format of the URLs which can be
        # more readable and natural-looking: project name is embedded
        # directly in the path and the query string contains other
@@ -365,6 +396,27 @@ our %feature = (
                'sub' => \&feature_patches,
                'override' => 0,
                'default' => [16]},
+
+       # Avatar support. When this feature is enabled, views such as
+       # shortlog or commit will display an avatar associated with
+       # the email of the committer(s) and/or author(s).
+
+       # Currently available providers are gravatar and picon.
+       # If an unknown provider is specified, the feature is disabled.
+
+       # Gravatar depends on Digest::MD5.
+       # Picon currently relies on the indiana.edu database.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'avatar'}{'default'} = ['<provider>'];
+       # where <provider> is either gravatar or picon.
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'avatar'}{'override'} = 1;
+       # and in project config gitweb.avatar = <provider>;
+       'avatar' => {
+               'sub' => \&feature_avatar,
+               'override' => 0,
+               'default' => ['']},
 );
 
 sub gitweb_get_feature {
@@ -376,7 +428,7 @@ sub gitweb_get_feature {
                @{$feature{$name}{'default'}});
        if (!$override) { return @defaults; }
        if (!defined $sub) {
-               warn "feature $name is not overrideable";
+               warn "feature $name is not overridable";
                return @defaults;
        }
        return $sub->(@defaults);
@@ -433,6 +485,12 @@ sub feature_patches {
        return ($_[0]);
 }
 
+sub feature_avatar {
+       my @val = (git_get_project_config('avatar'));
+
+       return @val ? @val : @_;
+}
+
 # checking HEAD file with -e is fragile if the repository was
 # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
 # and then pruned.
@@ -458,8 +516,9 @@ 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{$_} &&
+               !$known_snapshot_formats{$_}{'disabled'}} @fmts;
 }
 
 our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
@@ -690,9 +749,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;
@@ -813,6 +873,19 @@ $git_dir = "$projectroot/$project" if $project;
 our @snapshot_fmts = gitweb_get_feature('snapshot');
 @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
 
+# check that the avatar feature is set to a known provider name,
+# and for each provider check if the dependencies are satisfied.
+# if the provider name is invalid or the dependencies are not met,
+# reset $git_avatar to the empty string.
+our ($git_avatar) = gitweb_get_feature('avatar');
+if ($git_avatar eq 'gravatar') {
+       $git_avatar = '' unless (eval { require Digest::MD5; 1; });
+} elsif ($git_avatar eq 'picon') {
+       # no dependencies
+} else {
+       $git_avatar = '';
+}
+
 # dispatch
 if (!defined $action) {
        if (defined $hash) {
@@ -828,7 +901,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 +911,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;
@@ -891,10 +964,13 @@ sub href (%) {
                        if (defined $params{'hash_parent_base'}) {
                                $href .= esc_url($params{'hash_parent_base'});
                                # skip the file_parent if it's the same as the file_name
-                               delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'};
-                               if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) {
-                                       $href .= ":/".esc_url($params{'file_parent'});
-                                       delete $params{'file_parent'};
+                               if (defined $params{'file_parent'}) {
+                                       if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) {
+                                               delete $params{'file_parent'};
+                                       } elsif ($params{'file_parent'} !~ /\.\./) {
+                                               $href .= ":/".esc_url($params{'file_parent'});
+                                               delete $params{'file_parent'};
+                                       }
                                }
                                $href .= "..";
                                delete $params{'hash_parent'};
@@ -1036,7 +1112,7 @@ sub esc_url {
 }
 
 # replace invalid utf8 character with SUBSTITUTION sequence
-sub esc_html ($;%) {
+sub esc_html {
        my $str = shift;
        my %opts = @_;
 
@@ -1235,7 +1311,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 +1372,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,15 +1534,92 @@ 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);
+                      esc_html($short)) . $extra;
        } else {
                return $cgi->a({-href => $href, -class => "list subject"},
-                      esc_html($long)  . $extra);
+                      esc_html($long)) . $extra;
+       }
+}
+
+# Rather than recomputing the url for an email multiple times, we cache it
+# after the first hit. This gives a visible benefit in views where the avatar
+# for the same email is used repeatedly (e.g. shortlog).
+# The cache is shared by all avatar engines (currently gravatar only), which
+# are free to use it as preferred. Since only one avatar engine is used for any
+# given page, there's no risk for cache conflicts.
+our %avatar_cache = ();
+
+# Compute the picon url for a given email, by using the picon search service over at
+# http://www.cs.indiana.edu/picons/search.html
+sub picon_url {
+       my $email = lc shift;
+       if (!$avatar_cache{$email}) {
+               my ($user, $domain) = split('@', $email);
+               $avatar_cache{$email} =
+                       "http://www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
+                       "$domain/$user/" .
+                       "users+domains+unknown/up/single";
+       }
+       return $avatar_cache{$email};
+}
+
+# Compute the gravatar url for a given email, if it's not in the cache already.
+# Gravatar stores only the part of the URL before the size, since that's the
+# one computationally more expensive. This also allows reuse of the cache for
+# different sizes (for this particular engine).
+sub gravatar_url {
+       my $email = lc shift;
+       my $size = shift;
+       $avatar_cache{$email} ||=
+               "http://www.gravatar.com/avatar/" .
+                       Digest::MD5::md5_hex($email) . "?s=";
+       return $avatar_cache{$email} . $size;
+}
+
+# Insert an avatar for the given $email at the given $size if the feature
+# is enabled.
+sub git_get_avatar {
+       my ($email, %opts) = @_;
+       my $pre_white  = ($opts{-pad_before} ? "&nbsp;" : "");
+       my $post_white = ($opts{-pad_after}  ? "&nbsp;" : "");
+       $opts{-size} ||= 'default';
+       my $size = $avatar_size{$opts{-size}} || $avatar_size{'default'};
+       my $url = "";
+       if ($git_avatar eq 'gravatar') {
+               $url = gravatar_url($email, $size);
+       } elsif ($git_avatar eq 'picon') {
+               $url = picon_url($email);
+       }
+       # Other providers can be added by extending the if chain, defining $url
+       # as needed. If no variant puts something in $url, we assume avatars
+       # are completely disabled/unavailable.
+       if ($url) {
+               return $pre_white .
+                      "<img width=\"$size\" " .
+                           "class=\"avatar\" " .
+                           "src=\"$url\" " .
+                           "alt=\"\" " .
+                      "/>" . $post_white;
+       } else {
+               return "";
        }
 }
 
+# format the author name of the given commit with the given tag
+# the author name is chopped and escaped according to the other
+# optional parameters (see chop_str).
+sub format_author_html {
+       my $tag = shift;
+       my $co = shift;
+       my $author = chop_and_escape_str($co->{'author_name'}, @_);
+       return "<$tag class=\"author\">" .
+              git_get_avatar($co->{'author_email'}, -pad_after => 1) .
+              $author . "</$tag>";
+}
+
 # format git diff header line, i.e. "diff --(git|combined|cc) ..."
 sub format_git_diff_header_line {
        my $line = shift;
@@ -1838,7 +1991,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 +2203,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 +2218,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 +2281,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 +2339,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 +2402,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;
@@ -2398,8 +2550,14 @@ sub parse_tag {
                        $tag{'name'} = $1;
                } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) {
                        $tag{'author'} = $1;
-                       $tag{'epoch'} = $2;
-                       $tag{'tz'} = $3;
+                       $tag{'author_epoch'} = $2;
+                       $tag{'author_tz'} = $3;
+                       if ($tag{'author'} =~ m/^([^<]+) <([^>]*)>/) {
+                               $tag{'author_name'}  = $1;
+                               $tag{'author_email'} = $2;
+                       } else {
+                               $tag{'author_name'} = $tag{'author'};
+                       }
                } elsif ($line =~ m/--BEGIN/) {
                        push @comment, $line;
                        last;
@@ -2439,7 +2597,7 @@ sub parse_commit_text {
                } elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
                        push @parents, $1;
                } elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
-                       $co{'author'} = $1;
+                       $co{'author'} = to_utf8($1);
                        $co{'author_epoch'} = $2;
                        $co{'author_tz'} = $3;
                        if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -2449,10 +2607,9 @@ sub parse_commit_text {
                                $co{'author_name'} = $co{'author'};
                        }
                } elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) {
-                       $co{'committer'} = $1;
+                       $co{'committer'} = to_utf8($1);
                        $co{'committer_epoch'} = $2;
                        $co{'committer_tz'} = $3;
-                       $co{'committer_name'} = $co{'committer'};
                        if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) {
                                $co{'committer_name'}  = $1;
                                $co{'committer_email'} = $2;
@@ -2615,21 +2772,36 @@ 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;
 
-       #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa  panic.c'
-       $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+       if ($opts{'-l'}) {
+               #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa   16717  panic.c'
+               $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s;
 
-       $res{'mode'} = $1;
-       $res{'type'} = $2;
-       $res{'hash'} = $3;
-       if ($opts{'-z'}) {
-               $res{'name'} = $4;
+               $res{'mode'} = $1;
+               $res{'type'} = $2;
+               $res{'hash'} = $3;
+               $res{'size'} = $4;
+               if ($opts{'-z'}) {
+                       $res{'name'} = $5;
+               } else {
+                       $res{'name'} = unquote($5);
+               }
        } else {
-               $res{'name'} = unquote($4);
+               #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa  panic.c'
+               $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+
+               $res{'mode'} = $1;
+               $res{'type'} = $2;
+               $res{'hash'} = $3;
+               if ($opts{'-z'}) {
+                       $res{'name'} = $4;
+               } else {
+                       $res{'name'} = unquote($4);
+               }
        }
 
        return wantarray ? %res : \%res;
@@ -2804,18 +2976,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,22 +3385,54 @@ sub git_print_header_div {
              "\n</div>\n";
 }
 
-#sub git_print_authorship (\%) {
+sub print_local_time {
+       my %date = @_;
+       if ($date{'hour_local'} < 6) {
+               printf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
+                       $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
+       } else {
+               printf(" (%02d:%02d %s)",
+                       $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
+       }
+}
+
+# Outputs the author name and date in long form
 sub git_print_authorship {
        my $co = shift;
+       my %opts = @_;
+       my $tag = $opts{-tag} || 'div';
 
        my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});
-       print "<div class=\"author_date\">" .
+       print "<$tag class=\"author_date\">" .
              esc_html($co->{'author_name'}) .
              " [$ad{'rfc2822'}";
-       if ($ad{'hour_local'} < 6) {
-               printf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
-                      $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
-       } else {
-               printf(" (%02d:%02d %s)",
-                      $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
+       print_local_time(%ad) if ($opts{-localtime});
+       print "]" . git_get_avatar($co->{'author_email'}, -pad_before => 1)
+                 . "</$tag>\n";
+}
+
+# Outputs table rows containing the full author or committer information,
+# in the format expected for 'commit' view (& similia).
+# Parameters are a commit hash reference, followed by the list of people
+# to output information for. If the list is empty it defalts to both
+# author and committer.
+sub git_print_authorship_rows {
+       my $co = shift;
+       # too bad we can't use @people = @_ || ('author', 'committer')
+       my @people = @_;
+       @people = ('author', 'committer') unless @people;
+       foreach my $who (@people) {
+               my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"});
+               print "<tr><td>$who</td><td>" . esc_html($co->{$who}) . "</td>" .
+                     "<td rowspan=\"2\">" .
+                     git_get_avatar($co->{"${who}_email"}, -size => 'double') .
+                     "</td></tr>\n" .
+                     "<tr>" .
+                     "<td></td><td> $wd{'rfc2822'}";
+               print_local_time(%wd);
+               print "</td>" .
+                     "</tr>\n";
        }
-       print "]</div>\n";
 }
 
 sub git_print_page_path {
@@ -3269,8 +3473,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 +3531,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 +3544,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 '/');
@@ -3392,6 +3592,9 @@ sub git_print_tree_entry {
        # and link is the action links of the entry.
 
        print "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
+       if (exists $t->{'size'}) {
+               print "<td class=\"size\">$t->{'size'}</td>\n";
+       }
        if ($t->{'type'} eq "blob") {
                print "<td class=\"list\">" .
                        $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
@@ -3400,7 +3603,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,
@@ -3437,12 +3640,14 @@ sub git_print_tree_entry {
        } elsif ($t->{'type'} eq "tree") {
                print "<td class=\"list\">";
                print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
-                                            file_name=>"$basedir$t->{'name'}", %base_key)},
+                                            file_name=>"$basedir$t->{'name'}",
+                                            %base_key)},
                              esc_path($t->{'name'}));
                print "</td>\n";
                print "<td class=\"link\">";
                print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
-                                            file_name=>"$basedir$t->{'name'}", %base_key)},
+                                            file_name=>"$basedir$t->{'name'}",
+                                            %base_key)},
                              "tree");
                if (defined $hash_base) {
                        print " | " .
@@ -3993,7 +4198,7 @@ sub fill_project_list_info {
                            ($pname !~ /\/$/) &&
                            (-d "$projectroot/$pname")) {
                                $pr->{'forks'} = "-d $projectroot/$pname";
-                       }       else {
+                       } else {
                                $pr->{'forks'} = 0;
                        }
                }
@@ -4146,11 +4351,9 @@ sub git_shortlog_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               my $author = chop_and_escape_str($co{'author_name'}, 10);
                # git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
                print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-                     "<td><i>" . $author . "</i></td>\n" .
-                     "<td>";
+                     format_author_html('td', \%co, 10) . "<td>";
                print format_subject_html($co{'title'}, $co{'title_short'},
                                          href(action=>"commit", hash=>$commit), $ref);
                print "</td>\n" .
@@ -4197,11 +4400,9 @@ sub git_history_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-       # shortlog uses      chop_str($co{'author_name'}, 10)
-               my $author = chop_and_escape_str($co{'author_name'}, 15, 3);
                print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-                     "<td><i>" . $author . "</i></td>\n" .
-                     "<td>";
+       # shortlog:   format_author_html('td', \%co, 10)
+                     format_author_html('td', \%co, 15, 3) . "<td>";
                # originally git_history used chop_str($co{'title'}, 50)
                print format_subject_html($co{'title'}, $co{'title_short'},
                                          href(action=>"commit", hash=>$commit), $ref);
@@ -4354,9 +4555,8 @@ sub git_search_grep_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
                print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-                     "<td><i>" . $author . "</i></td>\n" .
+                     format_author_html('td', \%co, 15, 5) .
                      "<td>" .
                      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
                               -class => "list subject"},
@@ -4590,11 +4790,7 @@ sub git_tag {
                                              $tag{'type'}) . "</td>\n" .
              "</tr>\n";
        if (defined($tag{'author'})) {
-               my %ad = parse_date($tag{'epoch'}, $tag{'tz'});
-               print "<tr><td>author</td><td>" . esc_html($tag{'author'}) . "</td></tr>\n";
-               print "<tr><td></td><td>" . $ad{'rfc2822'} .
-                       sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) .
-                       "</td></tr>\n";
+               git_print_authorship_rows(\%tag, 'author');
        }
        print "</table>\n\n" .
              "</div>\n";
@@ -4651,7 +4847,7 @@ sub git_blame {
        git_print_page_path($file_name, $ftype, $hash_base);
 
        # page body
-       my @rev_color = qw(light2 dark2);
+       my @rev_color = qw(light dark);
        my $num_colors = scalar(@rev_color);
        my $current_color = 0;
        my %metainfo = ();
@@ -4669,15 +4865,18 @@ HTML
                my ($full_rev, $orig_lineno, $lineno, $group_size) =
                   ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
                if (!exists $metainfo{$full_rev}) {
-                       $metainfo{$full_rev} = {};
+                       $metainfo{$full_rev} = { 'nprevious' => 0 };
                }
                my $meta = $metainfo{$full_rev};
                my $data;
                while ($data = <$fd>) {
                        chomp $data;
                        last if ($data =~ s/^\t//); # contents of line
-                       if ($data =~ /^(\S+) (.*)$/) {
-                               $meta->{$1} = $2;
+                       if ($data =~ /^(\S+)(?: (.*))?$/) {
+                               $meta->{$1} = $2 unless exists $meta->{$1};
+                       }
+                       if ($data =~ /^previous /) {
+                               $meta->{'nprevious'}++;
                        }
                }
                my $short_rev = substr($full_rev, 0, 8);
@@ -4688,7 +4887,11 @@ HTML
                if ($group_size) {
                        $current_color = ($current_color + 1) % $num_colors;
                }
-               print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n";
+               my $tr_class = $rev_color[$current_color];
+               $tr_class .= ' boundary' if (exists $meta->{'boundary'});
+               $tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0);
+               $tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1);
+               print "<tr id=\"l$lineno\" class=\"$tr_class\">\n";
                if ($group_size) {
                        print "<td class=\"sha1\"";
                        print " title=\"". esc_html($author) . ", $date\"";
@@ -4698,22 +4901,31 @@ HTML
                                                     hash=>$full_rev,
                                                     file_name=>$file_name)},
                                      esc_html($short_rev));
+                       if ($group_size >= 2) {
+                               my @author_initials = ($author =~ /\b([[:upper:]])\B/g);
+                               if (@author_initials) {
+                                       print "<br />" .
+                                             esc_html(join('', @author_initials));
+                                       #           or join('.', ...)
+                               }
+                       }
                        print "</td>\n";
                }
-               my $parent_commit;
-               if (!exists $meta->{'parent'}) {
-                       open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
-                               or die_error(500, "Open git-rev-parse failed");
-                       $parent_commit = <$dd>;
-                       close $dd;
-                       chomp($parent_commit);
-                       $meta->{'parent'} = $parent_commit;
-               } else {
-                       $parent_commit = $meta->{'parent'};
-               }
+               # 'previous' <sha1 of parent commit> <filename at commit>
+               if (exists $meta->{'previous'} &&
+                   $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) {
+                       $meta->{'parent'} = $1;
+                       $meta->{'file_parent'} = unquote($2);
+               }
+               my $linenr_commit =
+                       exists($meta->{'parent'}) ?
+                       $meta->{'parent'} : $full_rev;
+               my $linenr_filename =
+                       exists($meta->{'file_parent'}) ?
+                       $meta->{'file_parent'} : unquote($meta->{'filename'});
                my $blamed = href(action => 'blame',
-                                 file_name => $meta->{'filename'},
-                                 hash_base => $parent_commit);
+                                 file_name => $linenr_filename,
+                                 hash_base => $linenr_commit);
                print "<td class=\"linenr\">";
                print $cgi->a({ -href => "$blamed#l$orig_lineno",
                                -class => "linenr" },
@@ -4803,11 +5015,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,18 +5120,25 @@ 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 $show_sizes = gitweb_check_feature('show-sizes');
+       my $have_blame = gitweb_check_feature('blame');
+
+       my @entries = ();
+       {
+               local $/ = "\0";
+               open my $fd, "-|", git_cmd(), "ls-tree", '-z',
+                       ($show_sizes ? '-l' : ()), @extra_options, $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);
        git_header_html();
        my $basedir = '';
-       my $have_blame = gitweb_check_feature('blame');
        if (defined $hash_base && (my %co = parse_commit($hash_base))) {
                my @views_nav = ();
                if (defined $file_name) {
@@ -4936,7 +5154,8 @@ sub git_tree {
                        # FIXME: Should be available when we have no hash base as well.
                        push @views_nav, $snapshot_links;
                }
-               git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
+               git_print_page_nav('tree','', $hash_base, undef, undef,
+                                  join(' | ', @views_nav));
                git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
        } else {
                undef $hash_base;
@@ -4969,8 +5188,10 @@ sub git_tree {
                undef $up unless $up;
                # based on git_print_tree_entry
                print '<td class="mode">' . mode_str('040000') . "</td>\n";
+               print '<td class="size">&nbsp;</td>'."\n" if $show_sizes;
                print '<td class="list">';
-               print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base,
+               print $cgi->a({-href => href(action=>"tree",
+                                            hash_base=>$hash_base,
                                             file_name=>$up)},
                              "..");
                print "</td>\n";
@@ -4979,7 +5200,7 @@ sub git_tree {
                print "</tr>\n";
        }
        foreach my $line (@entries) {
-               my %t = parse_ls_tree_line($line, -z => 1);
+               my %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes);
 
                if ($alternate) {
                        print "<tr class=\"dark\">\n";
@@ -5008,6 +5229,8 @@ sub git_snapshot {
                die_error(400, "Invalid snapshot format parameter");
        } elsif (!exists($known_snapshot_formats{$format})) {
                die_error(400, "Unknown snapshot format");
+       } elsif ($known_snapshot_formats{$format}{'disabled'}) {
+               die_error(403, "Snapshot format not allowed");
        } elsif (!grep($_ eq $format, @snapshot_fmts)) {
                die_error(403, "Unsupported snapshot format");
        }
@@ -5095,9 +5318,9 @@ sub git_log {
                      " | " .
                      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
                      "<br/>\n" .
-                     "</div>\n" .
-                     "<i>" . esc_html($co{'author_name'}) .  " [$ad{'rfc2822'}]</i><br/>\n" .
                      "</div>\n";
+                     git_print_authorship(\%co, -tag => 'span');
+                     print "<br/>\n</div>\n";
 
                print "<div class=\"log_body\">\n";
                git_print_log($co{'comment'}, -final_empty_line=> 1);
@@ -5116,8 +5339,6 @@ sub git_commit {
        $hash ||= $hash_base || "HEAD";
        my %co = parse_commit($hash)
            or die_error(404, "Unknown commit object");
-       my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
-       my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
 
        my $parent  = $co{'parent'};
        my $parents = $co{'parents'}; # listref
@@ -5146,7 +5367,7 @@ sub git_commit {
                        } @$parents ) .
                        ')';
        }
-       if (gitweb_check_feature('patches')) {
+       if (gitweb_check_feature('patches') && @$parents <= 1) {
                $formats_nav .= " | " .
                        $cgi->a({-href => href(action=>"patch", -replay=>1)},
                                "patch");
@@ -5184,22 +5405,7 @@ sub git_commit {
        }
        print "<div class=\"title_text\">\n" .
              "<table class=\"object_header\">\n";
-       print "<tr><td>author</td><td>" . esc_html($co{'author'}) . "</td></tr>\n".
-             "<tr>" .
-             "<td></td><td> $ad{'rfc2822'}";
-       if ($ad{'hour_local'} < 6) {
-               printf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
-                      $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
-       } else {
-               printf(" (%02d:%02d %s)",
-                      $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
-       }
-       print "</td>" .
-             "</tr>\n";
-       print "<tr><td>committer</td><td>" . esc_html($co{'committer'}) . "</td></tr>\n";
-       print "<tr><td></td><td> $cd{'rfc2822'}" .
-             sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) .
-             "</td></tr>\n";
+       git_print_authorship_rows(\%co);
        print "<tr><td>commit</td><td class=\"sha1\">$co{'id'}</td></tr>\n";
        print "<tr>" .
              "<td>tree</td>" .
@@ -5449,7 +5655,7 @@ sub git_commitdiff {
                $formats_nav =
                        $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
                                "raw");
-               if ($patch_max) {
+               if ($patch_max && @{$co{'parents'}} <= 1) {
                        $formats_nav .= " | " .
                                $cgi->a({-href => href(action=>"patch", -replay=>1)},
                                        "patch");
@@ -5580,7 +5786,11 @@ sub git_commitdiff {
                git_header_html(undef, $expires);
                git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
                git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);
-               git_print_authorship(\%co);
+               print "<div class=\"title_text\">\n" .
+                     "<table class=\"object_header\">\n";
+               git_print_authorship_rows(\%co);
+               print "</table>".
+                     "</div>\n";
                print "<div class=\"page_body\">\n";
                if (@{$co{'comment'}} > 1) {
                        print "<div class=\"log\">\n";
@@ -5653,7 +5863,7 @@ sub git_commitdiff_plain {
 
 # format-patch-style patches
 sub git_patch {
-       git_commitdiff(-format => 'patch', -single=> 1);
+       git_commitdiff(-format => 'patch', -single => 1);
 }
 
 sub git_patches {
@@ -5809,7 +6019,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 +6089,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 +6492,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..6746d422a98ed010489d4ce74b26a8a4600b183e 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 {
@@ -239,7 +225,12 @@ struct git_graph *graph_init(struct rev_info *opt)
        graph->num_columns = 0;
        graph->num_new_columns = 0;
        graph->mapping_size = 0;
-       graph->default_column_color = 0;
+       /*
+        * Start the column color at the maximum value, since we'll
+        * always increment it for the first commit we output.
+        * This way we start at 0 for the first commit.
+        */
+       graph->default_column_color = COLUMN_COLORS_MAX - 1;
 
        /*
         * Allocate a reasonably large default number of columns
@@ -300,9 +291,10 @@ static int graph_is_interesting(struct git_graph *graph, struct commit *commit)
        }
 
        /*
-        * Uninteresting and pruned commits won't be printed
+        * Otherwise, use get_commit_action() to see if this commit is
+        * interesting
         */
-       return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1;
+       return get_commit_action(graph->revs, commit) == commit_show;
 }
 
 static struct commit_list *next_interesting_parent(struct git_graph *graph,
@@ -513,11 +505,14 @@ static void graph_update_columns(struct git_graph *graph)
                             parent;
                             parent = next_interesting_parent(graph, parent)) {
                                /*
-                                * If this is a merge increment the current
+                                * If this is a merge, or the start of a new
+                                * childless column, increment the current
                                 * color.
                                 */
-                               if (graph->num_parents > 1)
+                               if (graph->num_parents > 1 ||
+                                   !is_commit_in_columns) {
                                        graph_increment_column_color(graph);
+                               }
                                graph_insert_into_new_columns(graph,
                                                              parent->item,
                                                              &mapping_idx);
@@ -907,7 +902,7 @@ static struct column *find_new_column_by_commit(struct git_graph *graph,
                if (graph->new_columns[i].commit == commit)
                        return &graph->new_columns[i];
        }
-       return 0;
+       return NULL;
 }
 
 static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
@@ -982,6 +977,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 +1017,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 +1054,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 +1087,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.c b/grep.c
index 04c777a20c1a8c10417cc9d44e53e5b99dc32a27..5d162dae6e43cdfcf6b20627df088a73cbfa98dc 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "grep.h"
+#include "userdiff.h"
 #include "xdiff-interface.h"
 
 void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)
@@ -305,6 +306,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
 {
        int hit = 0;
        int saved_ch = 0;
+       const char *start = bol;
 
        if ((p->token != GREP_PATTERN) &&
            ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
@@ -330,7 +332,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
 
        if (hit && p->word_regexp) {
                if ((pmatch[0].rm_so < 0) ||
-                   (eol - bol) <= pmatch[0].rm_so ||
+                   (eol - bol) < pmatch[0].rm_so ||
                    (pmatch[0].rm_eo < 0) ||
                    (eol - bol) < pmatch[0].rm_eo)
                        die("regexp returned nonsense");
@@ -349,6 +351,10 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
                else
                        hit = 0;
 
+               /* Words consist of at least one character. */
+               if (pmatch->rm_so == pmatch->rm_eo)
+                       hit = 0;
+
                if (!hit && pmatch[0].rm_so + bol + 1 < eol) {
                        /* There could be more than one match on the
                         * line, and the first match might not be
@@ -359,12 +365,17 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
                        bol = pmatch[0].rm_so + bol + 1;
                        while (word_char(bol[-1]) && bol < eol)
                                bol++;
+                       eflags |= REG_NOTBOL;
                        if (bol < eol)
                                goto again;
                }
        }
        if (p->token == GREP_PATTERN_HEAD && saved_ch)
                *eol = saved_ch;
+       if (hit) {
+               pmatch[0].rm_so += bol - start;
+               pmatch[0].rm_eo += bol - start;
+       }
        return hit;
 }
 
@@ -480,6 +491,17 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
 {
        int rest = eol - bol;
 
+       if (opt->pre_context || opt->post_context) {
+               if (opt->last_shown == 0) {
+                       if (opt->show_hunk_mark)
+                               fputs("--\n", stdout);
+                       else
+                               opt->show_hunk_mark = 1;
+               } else if (lno > opt->last_shown + 1)
+                       fputs("--\n", stdout);
+       }
+       opt->last_shown = lno;
+
        if (opt->null_following_name)
                sign = '\0';
        if (opt->pathname)
@@ -494,6 +516,8 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
 
                *eol = '\0';
                while (next_match(opt, bol, eol, ctx, &match, eflags)) {
+                       if (match.rm_so == match.rm_eo)
+                               break;
                        printf("%.*s%s%.*s%s",
                               (int)match.rm_so, bol,
                               opt->color_match,
@@ -508,22 +532,95 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        printf("%.*s\n", rest, bol);
 }
 
+static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
+{
+       xdemitconf_t *xecfg = opt->priv;
+       if (xecfg && xecfg->find_func) {
+               char buf[1];
+               return xecfg->find_func(bol, eol - bol, buf, 1,
+                                       xecfg->find_func_priv) >= 0;
+       }
+
+       if (bol == eol)
+               return 0;
+       if (isalpha(*bol) || *bol == '_' || *bol == '$')
+               return 1;
+       return 0;
+}
+
+static void show_funcname_line(struct grep_opt *opt, const char *name,
+                              char *buf, char *bol, unsigned lno)
+{
+       while (bol > buf) {
+               char *eol = --bol;
+
+               while (bol > buf && bol[-1] != '\n')
+                       bol--;
+               lno--;
+
+               if (lno <= opt->last_shown)
+                       break;
+
+               if (match_funcname(opt, bol, eol)) {
+                       show_line(opt, bol, eol, name, lno, '=');
+                       break;
+               }
+       }
+}
+
+static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
+                            char *bol, unsigned lno)
+{
+       unsigned cur = lno, from = 1, funcname_lno = 0;
+       int funcname_needed = opt->funcname;
+
+       if (opt->pre_context < lno)
+               from = lno - opt->pre_context;
+       if (from <= opt->last_shown)
+               from = opt->last_shown + 1;
+
+       /* Rewind. */
+       while (bol > buf && cur > from) {
+               char *eol = --bol;
+
+               while (bol > buf && bol[-1] != '\n')
+                       bol--;
+               cur--;
+               if (funcname_needed && match_funcname(opt, bol, eol)) {
+                       funcname_lno = cur;
+                       funcname_needed = 0;
+               }
+       }
+
+       /* We need to look even further back to find a function signature. */
+       if (opt->funcname && funcname_needed)
+               show_funcname_line(opt, name, buf, bol, cur);
+
+       /* Back forward. */
+       while (cur < lno) {
+               char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
+
+               while (*eol != '\n')
+                       eol++;
+               show_line(opt, bol, eol, name, cur, sign);
+               bol = eol + 1;
+               cur++;
+       }
+}
+
 static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         char *buf, unsigned long size, int collect_hits)
 {
        char *bol = buf;
        unsigned long left = size;
        unsigned lno = 1;
-       struct pre_context_line {
-               char *bol;
-               char *eol;
-       } *prev = NULL, *pcl;
        unsigned last_hit = 0;
-       unsigned last_shown = 0;
        int binary_match_only = 0;
-       const char *hunk_mark = "";
        unsigned count = 0;
        enum grep_context ctx = GREP_CONTEXT_HEAD;
+       xdemitconf_t xecfg;
+
+       opt->last_shown = 0;
 
        if (buffer_is_binary(buf, size)) {
                switch (opt->binary) {
@@ -538,10 +635,16 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                }
        }
 
-       if (opt->pre_context)
-               prev = xcalloc(opt->pre_context, sizeof(*prev));
-       if (opt->pre_context || opt->post_context)
-               hunk_mark = "--\n";
+       memset(&xecfg, 0, sizeof(xecfg));
+       if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
+           !opt->name_only && !binary_match_only && !collect_hits) {
+               struct userdiff_driver *drv = userdiff_find_by_path(name);
+               if (drv && drv->funcname.pattern) {
+                       const struct userdiff_funcname *pe = &drv->funcname;
+                       xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
+                       opt->priv = &xecfg;
+               }
+       }
 
        while (left) {
                char *eol, ch;
@@ -589,45 +692,20 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         * the context which is nonsense, but the user
                         * deserves to get that ;-).
                         */
-                       if (opt->pre_context) {
-                               unsigned from;
-                               if (opt->pre_context < lno)
-                                       from = lno - opt->pre_context;
-                               else
-                                       from = 1;
-                               if (from <= last_shown)
-                                       from = last_shown + 1;
-                               if (last_shown && from != last_shown + 1)
-                                       fputs(hunk_mark, stdout);
-                               while (from < lno) {
-                                       pcl = &prev[lno-from-1];
-                                       show_line(opt, pcl->bol, pcl->eol,
-                                                 name, from, '-');
-                                       from++;
-                               }
-                               last_shown = lno-1;
-                       }
-                       if (last_shown && lno != last_shown + 1)
-                               fputs(hunk_mark, stdout);
+                       if (opt->pre_context)
+                               show_pre_context(opt, name, buf, bol, lno);
+                       else if (opt->funcname)
+                               show_funcname_line(opt, name, buf, bol, lno);
                        if (!opt->count)
                                show_line(opt, bol, eol, name, lno, ':');
-                       last_shown = last_hit = lno;
+                       last_hit = lno;
                }
                else if (last_hit &&
                         lno <= last_hit + opt->post_context) {
                        /* If the last hit is within the post context,
                         * we need to show this line.
                         */
-                       if (last_shown && lno != last_shown + 1)
-                               fputs(hunk_mark, stdout);
                        show_line(opt, bol, eol, name, lno, '-');
-                       last_shown = lno;
-               }
-               if (opt->pre_context) {
-                       memmove(prev+1, prev,
-                               (opt->pre_context-1) * sizeof(*prev));
-                       prev->bol = bol;
-                       prev->eol = eol;
                }
 
        next_line:
@@ -638,7 +716,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                lno++;
        }
 
-       free(prev);
        if (collect_hits)
                return 0;
 
@@ -650,6 +727,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                return 1;
        }
 
+       xdiff_clear_find_func(&xecfg);
+       opt->priv = NULL;
+
        /* NEEDSWORK:
         * The real "grep -c foo *.c" gives many "bar.c:0" lines,
         * which feels mostly useless but sometimes useful.  Maybe
diff --git a/grep.h b/grep.h
index a67005de62d1442e7ba6a8dc27320225a0d55819..f6eecc62c038f212fa4efc8648f13f61bf937452 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -59,31 +59,37 @@ struct grep_opt {
        struct grep_pat *pattern_list;
        struct grep_pat **pattern_tail;
        struct grep_expr *pattern_expression;
+       const char *prefix;
        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;
+       int max_depth;
+       int funcname;
        char color_match[COLOR_MAXLEN];
        const char *color_external;
        int regflags;
        unsigned pre_context;
        unsigned post_context;
+       unsigned last_shown;
+       int show_hunk_mark;
+       void *priv;
 };
 
 extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
index ebb3bedb074202a29e2356845012bd1ffae0dc19..9455dd0709aeb81436ef2e3e36422578d66ce8e7 100644 (file)
@@ -29,7 +29,7 @@ static void hash_object(const char *path, const char *type, int write_object,
        int fd;
        fd = open(path, O_RDONLY);
        if (fd < 0)
-               die("Cannot open %s", path);
+               die_errno("Cannot open '%s'", path);
        hash_fd(fd, type, write_object, vpath);
 }
 
@@ -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();
diff --git a/help.c b/help.c
index fd87bb5aeec82beec600be46248b19b13bb33804..e8db31f60f010887ccb3a943344da566e0937403 100644 (file)
--- a/help.c
+++ b/help.c
@@ -100,7 +100,7 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)
 
        if (space < max_cols)
                cols = max_cols / space;
-       rows = (cmds->cnt + cols - 1) / cols;
+       rows = DIV_ROUND_UP(cmds->cnt, cols);
 
        for (i = 0; i < rows; i++) {
                printf("  ");
@@ -126,8 +126,8 @@ static int is_executable(const char *name)
            !S_ISREG(st.st_mode))
                return 0;
 
-#ifdef __MINGW32__
-       /* cannot trust the executable bit, peek into the file instead */
+#ifdef WIN32
+{      /* cannot trust the executable bit, peek into the file instead */
        char buf[3] = { 0 };
        int n;
        int fd = open(name, O_RDONLY);
@@ -140,6 +140,7 @@ static int is_executable(const char *name)
                                st.st_mode |= S_IXUSR;
                close(fd);
        }
+}
 #endif
        return st.st_mode & S_IXUSR;
 }
@@ -302,7 +303,7 @@ const char *help_unknown_cmd(const char *cmd)
        struct cmdnames main_cmds, other_cmds;
 
        memset(&main_cmds, 0, sizeof(main_cmds));
-       memset(&other_cmds, 0, sizeof(main_cmds));
+       memset(&other_cmds, 0, sizeof(other_cmds));
        memset(&aliases, 0, sizeof(aliases));
 
        git_config(git_unknown_cmd_config, NULL);
@@ -334,7 +335,7 @@ const char *help_unknown_cmd(const char *cmd)
                const char *assumed = main_cmds.names[0]->name;
                main_cmds.names[0] = NULL;
                clean_cmdnames(&main_cmds);
-               fprintf(stderr, "WARNING: You called a Git program named '%s', "
+               fprintf(stderr, "WARNING: You called a Git command named '%s', "
                        "which does not exist.\n"
                        "Continuing under the assumption that you meant '%s'\n",
                        cmd, assumed);
similarity index 95%
rename from builtin-http-fetch.c
rename to http-fetch.c
index f3e63d7206604029504aaf85b3d2e7731d054d2c..e8f44babd9d3a1cd86eba659b054aed6ac2c792a 100644 (file)
@@ -1,8 +1,9 @@
 #include "cache.h"
 #include "walker.h"
 
-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+int main(int argc, const char **argv)
 {
+       const char *prefix;
        struct walker *walker;
        int commits_on_stdin = 0;
        int commits;
@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
        int get_verbosely = 0;
        int get_recover = 0;
 
+       prefix = setup_git_directory();
+
        git_config(git_default_config, NULL);
 
        while (arg < argc && argv[arg][0] == '-') {
index 5138224cc372482fb60afd350fa40e37d019f367..00e83dcec1d973b069d4c75105aed96634b00994 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;
@@ -206,6 +193,8 @@ static char *xml_entities(char *s)
                case '&':
                        strbuf_addstr(&buf, "&amp;");
                        break;
+               case 0:
+                       return strbuf_detach(&buf, NULL);
                }
                s++;
        }
@@ -237,15 +226,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 +239,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(prevfile);
-       rename(request->tmpfile, prevfile);
-       unlink(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(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 +295,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 +311,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 +520,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 +582,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(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(request->tmpfile);
-                       } else if (hashcmp(request->obj->sha1, request->real_sha1)) {
-                               unlink(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 +664,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 +702,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 +714,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)
@@ -1415,8 +1032,9 @@ static void remove_locks(void)
 
        fprintf(stderr, "Removing remote locks...\n");
        while (lock) {
+               struct remote_lock *next = lock->next;
                unlock_remote(lock);
-               lock = lock->next;
+               lock = next;
        }
 }
 
@@ -1843,7 +1461,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)
 {
@@ -1873,27 +1491,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;
@@ -1987,29 +1593,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;
 }
@@ -2018,27 +1617,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);
@@ -2119,13 +1704,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))
@@ -2167,6 +1752,25 @@ static int delete_remote_branch(char *pattern, int force)
        return 0;
 }
 
+static 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;
@@ -2211,6 +1815,7 @@ int main(int argc, char **argv)
                        }
                        if (!strcmp(arg, "--verbose")) {
                                push_verbosely = 1;
+                               http_is_verbose = 1;
                                continue;
                        }
                        if (!strcmp(arg, "-d")) {
@@ -2260,8 +1865,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);
@@ -2271,6 +1874,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;
@@ -2300,6 +1907,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) {
@@ -2310,9 +1918,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;
@@ -2326,14 +1932,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;
@@ -2349,7 +1955,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,
@@ -2399,13 +2005,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 */
@@ -2429,16 +2036,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))
@@ -2467,8 +2066,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 c5a3ea3b31045be9407579a9d1aba222ee3e9914..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(prevfile);
-       rename(obj_req->tmpfile, prevfile);
-       unlink(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(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(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(obj_req->tmpfile);
+       if (finish_http_object_request(obj_req->req))
                return;
-       }
-       if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
-               unlink(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(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..23b2a1932c7daebd585827da7effd1c5bbae5e69 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
@@ -27,7 +33,19 @@ static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static char *user_name, *user_pass;
 
+#if LIBCURL_VERSION_NUM >= 0x071700
+/* Use CURLOPT_KEYPASSWD as is */
+#elif LIBCURL_VERSION_NUM >= 0x070903
+#define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
+#else
+#define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
+#endif
+
+static char *ssl_cert_password;
+static int ssl_cert_password_required;
+
 static struct curl_slist *pragma_header;
+static struct curl_slist *no_pragma_header;
 
 static struct active_request_slot *active_queue_head;
 
@@ -119,7 +137,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
@@ -129,6 +147,11 @@ static int http_options(const char *var, const char *value, void *cb)
 #endif
        if (!strcmp("http.sslcainfo", var))
                return git_config_string(&ssl_cainfo, var, value);
+       if (!strcmp("http.sslcertpasswordprotected", var)) {
+               if (git_config_bool(var, value))
+                       ssl_cert_password_required = 1;
+               return 0;
+       }
 #ifdef USE_CURL_MULTI
        if (!strcmp("http.maxrequests", var)) {
                max_requests = git_config_int(var, value);
@@ -167,6 +190,22 @@ static void init_curl_http_auth(CURL *result)
        }
 }
 
+static int has_cert_password(void)
+{
+       if (ssl_cert_password != NULL)
+               return 1;
+       if (ssl_cert == NULL || ssl_cert_password_required != 1)
+               return 0;
+       /* Only prompt the user once. */
+       ssl_cert_password_required = -1;
+       ssl_cert_password = getpass("Certificate Password: ");
+       if (ssl_cert_password != NULL) {
+               ssl_cert_password = xstrdup(ssl_cert_password);
+               return 1;
+       } else
+               return 0;
+}
+
 static CURL *get_curl_handle(void)
 {
        CURL *result = curl_easy_init();
@@ -189,7 +228,9 @@ static CURL *get_curl_handle(void)
 
        if (ssl_cert != NULL)
                curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
-#if LIBCURL_VERSION_NUM >= 0x070902
+       if (has_cert_password())
+               curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password);
+#if LIBCURL_VERSION_NUM >= 0x070903
        if (ssl_key != NULL)
                curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
 #endif
@@ -276,6 +317,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 +327,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 +347,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
@@ -329,8 +373,13 @@ void http_init(struct remote *remote)
        if (getenv("GIT_CURL_FTP_NO_EPSV"))
                curl_ftp_no_epsv = 1;
 
-       if (remote && remote->url && remote->url[0])
+       if (remote && remote->url && remote->url[0]) {
                http_auth_init(remote->url[0]);
+               if (!ssl_cert_password_required &&
+                   getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
+                   !prefixcmp(remote->url[0], "https://"))
+                       ssl_cert_password_required = 1;
+       }
 
 #ifndef NO_CURL_EASY_DUPHANDLE
        curl_default = get_curl_handle();
@@ -366,10 +415,20 @@ 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;
        }
+
+       if (ssl_cert_password != NULL) {
+               memset(ssl_cert_password, 0, strlen(ssl_cert_password));
+               free(ssl_cert_password);
+               ssl_cert_password = NULL;
+       }
+       ssl_cert_password_required = 0;
 }
 
 struct active_request_slot *get_active_slot(void)
@@ -611,6 +670,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 +691,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 +715,573 @@ 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)
+{
+       end_url_with_slash(buf, url);
+
+       strbuf_addf(buf, "objects/%.*s/", 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 = NULL;
+       struct strbuf buf = STRBUF_INIT;
+
+       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 *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));
+       preq->url = strbuf_detach(&buf, NULL);
+
+       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, preq->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);
+       free(preq->url);
+       free(preq);
+       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];
+       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);
+
+       freq->url = get_remote_object_url(base_url, hex, 0);
+
+       /*
+        * 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);
+                       if (ftruncate(freq->localfile, 0) < 0) {
+                               error("Couldn't truncate temporary file %s for %s: %s",
+                                         freq->tmpfile, freq->filename, strerror(errno));
+                               goto abort;
+                       }
+               }
+       }
+
+       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, freq->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;
+
+abort:
+       free(filename);
+       free(freq->url);
+       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;
+       }
+       if (freq->slot != NULL) {
+               freq->slot->callback_func = NULL;
+               freq->slot->callback_data = NULL;
+               release_active_slot(freq->slot);
+               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..3847fd151df6e9f41fdb0b95573882a7d9ac992a 100644 (file)
@@ -238,7 +238,7 @@ static const char *Flags[] = {
 #ifndef NO_OPENSSL
 static void ssl_socket_perror(const char *func)
 {
-       fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0));
+       fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), NULL));
 }
 #endif
 
@@ -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..b4f82786592e2e34392191e823d645e17d955fcf 100644 (file)
@@ -143,7 +143,7 @@ static void *fill(int min)
                if (ret <= 0) {
                        if (!ret)
                                die("early EOF");
-                       die("read error on input: %s", strerror(errno));
+                       die_errno("read error on input");
                }
                input_len += ret;
                if (from_stdin)
@@ -178,13 +178,12 @@ static char *open_pack_file(char *pack_name)
                } else
                        output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
                if (output_fd < 0)
-                       die("unable to create %s: %s", pack_name, strerror(errno));
+                       die_errno("unable to create '%s'", pack_name);
                pack_fd = output_fd;
        } else {
                input_fd = open(pack_name, O_RDONLY);
                if (input_fd < 0)
-                       die("cannot open packfile '%s': %s",
-                           pack_name, strerror(errno));
+                       die_errno("cannot open packfile '%s'", pack_name);
                output_fd = -1;
                pack_fd = input_fd;
        }
@@ -207,8 +206,8 @@ static void parse_pack_header(void)
        use(sizeof(struct pack_header));
 }
 
-static void bad_object(unsigned long offset, const char *format,
-                      ...) NORETURN __attribute__((format (printf, 2, 3)));
+static NORETURN void bad_object(unsigned long offset, const char *format,
+                      ...) __attribute__((format (printf, 2, 3)));
 
 static void bad_object(unsigned long offset, const char *format, ...)
 {
@@ -293,8 +292,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 +311,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;
@@ -370,7 +369,7 @@ static void *get_data_from_pack(struct object_entry *obj)
        do {
                ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
                if (n < 0)
-                       die("cannot pread pack file: %s", strerror(errno));
+                       die_errno("cannot pread pack file");
                if (!n)
                        die("premature end of pack file, %lu bytes missing",
                            len - rdy);
@@ -469,7 +468,7 @@ static void sha1_object(const void *data, unsigned long size,
                                die("invalid %s", typename(type));
                        if (fsck_object(obj, 1, fsck_error_function))
                                die("Error in object");
-                       if (fsck_walk(obj, mark_link, 0))
+                       if (fsck_walk(obj, mark_link, NULL))
                                die("Not all child objects of %s are reachable", sha1_to_hex(obj->sha1));
 
                        if (obj->type == OBJ_TREE) {
@@ -631,7 +630,7 @@ static void parse_pack_objects(unsigned char *sha1)
 
        /* If input_fd is a file, we should have reached its end now. */
        if (fstat(input_fd, &st))
-               die("cannot fstat packfile: %s", strerror(errno));
+               die_errno("cannot fstat packfile");
        if (S_ISREG(st.st_mode) &&
                        lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
                die("pack has junk at the end");
@@ -788,7 +787,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                fsync_or_die(output_fd, curr_pack_name);
                err = close(output_fd);
                if (err)
-                       die("error while closing pack file: %s", strerror(errno));
+                       die_errno("error while closing pack file");
        }
 
        if (keep_msg) {
@@ -801,16 +800,16 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
 
                if (keep_fd < 0) {
                        if (errno != EEXIST)
-                               die("cannot write keep file '%s' (%s)",
-                                   keep_name, strerror(errno));
+                               die_errno("cannot write keep file '%s'",
+                                         keep_name);
                } else {
                        if (keep_msg_len > 0) {
                                write_or_die(keep_fd, keep_msg, keep_msg_len);
                                write_or_die(keep_fd, "\n", 1);
                        }
                        if (close(keep_fd) != 0)
-                               die("cannot close written keep file '%s' (%s)",
-                                   keep_name, strerror(errno));
+                               die_errno("cannot close written keep file '%s'",
+                                   keep_name);
                        report = "keep";
                }
        }
index fa2ca5250c75a0d335570a3a3c71b20ebd0b42fa..2d6b6d6cb1d2bc2d334bf058feb3444e94b5a781 100644 (file)
@@ -55,7 +55,7 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
 
 static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
                        mmbuffer_t *result,
-                       const char *path_unused,
+                       const char *path,
                        mmfile_t *orig,
                        mmfile_t *src1, const char *name1,
                        mmfile_t *src2, const char *name2,
@@ -67,10 +67,10 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
        if (buffer_is_binary(orig->ptr, orig->size) ||
            buffer_is_binary(src1->ptr, src1->size) ||
            buffer_is_binary(src2->ptr, src2->size)) {
-               warning("Cannot merge binary files: %s vs. %s\n",
-                       name1, name2);
+               warning("Cannot merge binary files: %s (%s vs. %s)\n",
+                       path, name1, name2);
                return ll_binary_merge(drv_unused, result,
-                                      path_unused,
+                                      path,
                                       orig, src1, name1,
                                       src2, name2,
                                       virtual_ancestor);
@@ -152,7 +152,7 @@ static void create_temp(mmfile_t *src, char *path)
        strcpy(path, ".merge_file_XXXXXX");
        fd = xmkstemp(path);
        if (write_in_full(fd, src->ptr, src->size) != src->size)
-               die("unable to write temp-file");
+               die_errno("unable to write temp-file");
        close(fd);
 }
 
@@ -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,18 +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);
-       if (status < -ERR_RUN_COMMAND_FORK)
-               ; /* failure in run-command */
-       else
-               status = -status;
+       status = run_command_v_opt(args, 0);
        fd = open(temp[1], O_RDONLY);
        if (fd < 0)
                goto bad;
@@ -219,7 +208,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
        close(fd);
  bad:
        for (i = 0; i < 3; i++)
-               unlink(temp[i]);
+               unlink_or_warn(temp[i]);
        strbuf_release(&cmd);
        return status;
 }
@@ -238,7 +227,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 +261,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 +284,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 828d19f452a8bfffe2ee224a630e8246f4a1edab..6851fa55a503bd24dd204934d7433e306996ce09 100644 (file)
@@ -16,7 +16,7 @@ static void remove_lock_file(void)
                    lock_file_list->filename[0]) {
                        if (lock_file_list->fd >= 0)
                                close(lock_file_list->fd);
-                       unlink(lock_file_list->filename);
+                       unlink_or_warn(lock_file_list->filename);
                }
                lock_file_list = lock_file_list->next;
        }
@@ -155,18 +155,32 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
        return lk->fd;
 }
 
-
-NORETURN void unable_to_lock_index_die(const char *path, int err)
+static char *unable_to_lock_message(const char *path, int err)
 {
+       struct strbuf buf = STRBUF_INIT;
+
        if (err == EEXIST) {
-               die("Unable to create '%s.lock': %s.\n\n"
+               strbuf_addf(&buf, "Unable to create '%s.lock': %s.\n\n"
                    "If no other git process is currently running, this probably means a\n"
                    "git process crashed in this repository earlier. Make sure no other git\n"
                    "process is running and remove the file manually to continue.",
                    path, strerror(err));
-       } else {
-               die("Unable to create '%s.lock': %s", path, strerror(err));
-       }
+       } else
+               strbuf_addf(&buf, "Unable to create '%s.lock': %s", path, strerror(err));
+       return strbuf_detach(&buf, NULL);
+}
+
+int unable_to_lock_error(const char *path, int err)
+{
+       char *msg = unable_to_lock_message(path, err);
+       error("%s", msg);
+       free(msg);
+       return -1;
+}
+
+NORETURN void unable_to_lock_index_die(const char *path, int err)
+{
+       die("%s", unable_to_lock_message(path, err));
 }
 
 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
@@ -259,7 +273,7 @@ void rollback_lock_file(struct lock_file *lk)
        if (lk->filename[0]) {
                if (lk->fd >= 0)
                        close(lk->fd);
-               unlink(lk->filename);
+               unlink_or_warn(lk->filename);
        }
        lk->filename[0] = 0;
 }
index 5bd29e6994c92268ec576671bb8564b57d1a5c9d..f7d54f2f1b132d54adda6a9ca5f8e1332856709f 100644 (file)
@@ -25,6 +25,8 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        struct object *obj = parse_object(sha1);
        if (!obj)
                return 0;
+       if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
+               refname = prettify_refname(refname);
        add_name_decoration("", refname, obj);
        while (obj->type == OBJ_TAG) {
                obj = ((struct tag *)obj)->tagged;
@@ -35,12 +37,13 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        return 0;
 }
 
-void load_ref_decorations(void)
+void load_ref_decorations(int flags)
 {
        static int loaded;
        if (!loaded) {
                loaded = 1;
-               for_each_ref(add_ref_decoration, NULL);
+               for_each_ref(add_ref_decoration, &flags);
+               head_ref(add_ref_decoration, &flags);
        }
 }
 
@@ -167,18 +170,6 @@ static unsigned int digits_in_number(unsigned int number)
        return result;
 }
 
-static int has_non_ascii(const char *s)
-{
-       int ch;
-       if (!s)
-               return 0;
-       while ((ch = *s++) != '\0') {
-               if (non_ascii(ch))
-                       return 1;
-       }
-       return 0;
-}
-
 void get_patch_filename(struct commit *commit, int nr, const char *suffix,
                        struct strbuf *buf)
 {
@@ -320,7 +311,8 @@ void show_log(struct rev_info *opt)
        }
 
        /*
-        * If use_terminator is set, add a newline at the end of the entry.
+        * If use_terminator is set, we already handled any record termination
+        * at the end of the last record.
         * Otherwise, add a diffopt.line_termination character before all
         * entries but the first.  (IOW, as a separator between entries)
         */
@@ -399,7 +391,9 @@ void show_log(struct rev_info *opt)
                         */
                        show_reflog_message(opt->reflog_info,
                                    opt->commit_format == CMIT_FMT_ONELINE,
-                                   opt->date_mode);
+                                   opt->date_mode_explicit ?
+                                       opt->date_mode :
+                                       DATE_NORMAL);
                        if (opt->commit_format == CMIT_FMT_ONELINE)
                                return;
                }
index 20b5caf1aa45aa0ba076ec60320d252c42abfb64..3f7b40027b7203985f018e43ab72a6cfc233e8d7 100644 (file)
@@ -17,7 +17,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                             const char **subject_p,
                             const char **extra_headers_p,
                             int *need_8bit_cte_p);
-void load_ref_decorations(void);
+void load_ref_decorations(int flags);
 
 #define FORMAT_PATCH_NAME_MAX 64
 void get_patch_filename(struct commit *commit, int nr, const char *suffix,
index bb1f2fb711a588d2af0d61decbd4b3eb2f2aebbe..f167c005bf9039f44d21ef5f32ab27db7784d642 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -103,7 +103,7 @@ static char *parse_name_and_email(char *buffer, char **name,
                char **email, int allow_empty_email)
 {
        char *left, *right, *nstart, *nend;
-       *name = *email = 0;
+       *name = *email = NULL;
 
        if ((left = strchr(buffer, '<')) == NULL)
                return NULL;
@@ -136,7 +136,7 @@ static int read_single_mailmap(struct string_list *map, const char *filename, ch
        if (f == NULL)
                return 1;
        while (fgets(buffer, sizeof(buffer), f) != NULL) {
-               char *name1 = 0, *email1 = 0, *name2 = 0, *email2 = 0;
+               char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
                if (buffer[0] == '#') {
                        static const char abbrev[] = "# repo-abbrev:";
                        int abblen = sizeof(abbrev) - 1;
@@ -200,7 +200,7 @@ int map_user(struct string_list *map,
        if (!p) {
                /* email passed in might not be wrapped in <>, but end with a \0 */
                p = memchr(email, '\0', maxlen_email);
-               if (p == 0)
+               if (!p)
                        return 0;
        }
        if (p - email + 1 < sizeof(buf))
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;
 }
 
index a3721efcaf9458e5c735b45794d60bac670366d2..f55b7ebe11bbd54f00a84dd1eb9d34f60d74a1c3 100644 (file)
@@ -38,7 +38,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two)
  * A virtual commit has (const char *)commit->util set to the name.
  */
 
-struct commit *make_virtual_commit(struct tree *tree, const char *comment)
+static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
 {
        struct commit *commit = xcalloc(1, sizeof(struct commit));
        commit->tree = tree;
@@ -170,6 +170,18 @@ static int git_merge_trees(int index_only,
        int rc;
        struct tree_desc t[3];
        struct unpack_trees_options opts;
+       static const struct unpack_trees_error_msgs msgs = {
+               /* would_overwrite */
+               "Your local changes to '%s' would be overwritten by merge.  Aborting.",
+               /* not_uptodate_file */
+               "Your local changes to '%s' would be overwritten by merge.  Aborting.",
+               /* not_uptodate_dir */
+               "Updating '%s' would lose untracked files in it.  Aborting.",
+               /* would_lose_untracked */
+               "Untracked working tree file '%s' would be %s by merge.  Aborting",
+               /* bind_overlap -- will not happen here */
+               NULL,
+       };
 
        memset(&opts, 0, sizeof(opts));
        if (index_only)
@@ -181,6 +193,7 @@ static int git_merge_trees(int index_only,
        opts.fn = threeway_merge;
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
+       opts.msgs = msgs;
 
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
@@ -438,7 +451,7 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
                        /* Ignore epipe */
                        if (errno == EPIPE)
                                break;
-                       die("merge-recursive: %s", strerror(errno));
+                       die_errno("merge-recursive");
                } else if (!ret) {
                        die("merge-recursive: disk full?");
                }
@@ -554,7 +567,7 @@ static void update_file_flags(struct merge_options *o,
                                mode = 0666;
                        fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
                        if (fd < 0)
-                               die("failed to open %s: %s", path, strerror(errno));
+                               die_errno("failed to open '%s'", path);
                        flush_buffer(fd, buf, size);
                        close(fd);
                } else if (S_ISLNK(mode)) {
@@ -562,7 +575,7 @@ static void update_file_flags(struct merge_options *o,
                        safe_create_leading_directories_const(path);
                        unlink(path);
                        if (symlink(lnk, path))
-                               die("failed to symlink %s: %s", path, strerror(errno));
+                               die_errno("failed to symlink '%s'", path);
                        free(lnk);
                } else
                        die("do not know what to do with %06o %s '%s'",
@@ -622,8 +635,13 @@ static int merge_3way(struct merge_options *o,
        char *name1, *name2;
        int merge_status;
 
-       name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
-       name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
+       if (strcmp(a->path, b->path)) {
+               name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
+               name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
+       } else {
+               name1 = xstrdup(mkpath("%s", branch1));
+               name2 = xstrdup(mkpath("%s", branch2));
+       }
 
        fill_mm(one->sha1, &orig);
        fill_mm(a->sha1, &src1);
@@ -933,11 +951,12 @@ static int process_renames(struct merge_options *o,
                                       ren1_src, ren1_dst, branch1,
                                       branch2);
                                update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
-                               update_stages(ren1_dst, NULL,
-                                               branch1 == o->branch1 ?
-                                               ren1->pair->two : NULL,
-                                               branch1 == o->branch1 ?
-                                               NULL : ren1->pair->two, 1);
+                               if (!o->call_depth)
+                                       update_stages(ren1_dst, NULL,
+                                                       branch1 == o->branch1 ?
+                                                       ren1->pair->two : NULL,
+                                                       branch1 == o->branch1 ?
+                                                       NULL : ren1->pair->two, 1);
                        } else if (!sha_eq(dst_other.sha1, null_sha1)) {
                                const char *new_path;
                                clean_merge = 0;
@@ -946,9 +965,31 @@ static int process_renames(struct merge_options *o,
                                       "%s added in %s",
                                       ren1_src, ren1_dst, branch1,
                                       ren1_dst, branch2);
-                               new_path = unique_path(o, ren1_dst, branch2);
-                               output(o, 1, "Adding as %s instead", new_path);
-                               update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
+                               if (o->call_depth) {
+                                       struct merge_file_info mfi;
+                                       struct diff_filespec one, a, b;
+
+                                       one.path = a.path = b.path =
+                                               (char *)ren1_dst;
+                                       hashcpy(one.sha1, null_sha1);
+                                       one.mode = 0;
+                                       hashcpy(a.sha1, ren1->pair->two->sha1);
+                                       a.mode = ren1->pair->two->mode;
+                                       hashcpy(b.sha1, dst_other.sha1);
+                                       b.mode = dst_other.mode;
+                                       mfi = merge_file(o, &one, &a, &b,
+                                                        branch1,
+                                                        branch2);
+                                       output(o, 1, "Adding merged %s", ren1_dst);
+                                       update_file(o, 0,
+                                                   mfi.sha,
+                                                   mfi.mode,
+                                                   ren1_dst);
+                               } else {
+                                       new_path = unique_path(o, ren1_dst, branch2);
+                                       output(o, 1, "Adding as %s instead", new_path);
+                                       update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
+                               }
                        } else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
                                ren2 = item->util;
                                clean_merge = 0;
@@ -1160,10 +1201,14 @@ int merge_trees(struct merge_options *o,
 
        code = git_merge_trees(o->call_depth, common, head, merge);
 
-       if (code != 0)
-               die("merging of trees %s and %s failed",
-                   sha1_to_hex(head->object.sha1),
-                   sha1_to_hex(merge->object.sha1));
+       if (code != 0) {
+               if (show(o, 4) || o->call_depth)
+                       die("merging of trees %s and %s failed",
+                           sha1_to_hex(head->object.sha1),
+                           sha1_to_hex(merge->object.sha1));
+               else
+                       exit(128);
+       }
 
        if (unmerged_cache()) {
                struct string_list *entries, *re_head, *re_merge;
diff --git a/mktag.c b/mktag.c
index 99a356e9ee75cb247d80ed6dc0b251ceb0bd9e46..a3b4270c18ea78fa36f7243de5a9e05e2066e030 100644 (file)
--- a/mktag.c
+++ b/mktag.c
 /*
  * We refuse to tag something we can't verify. Just because.
  */
-static int verify_object(unsigned char *sha1, const char *expected_type)
+static int verify_object(const unsigned char *sha1, const char *expected_type)
 {
        int ret = -1;
        enum object_type type;
        unsigned long size;
-       void *buffer = read_sha1_file(sha1, &type, &size);
+       const unsigned char *repl;
+       void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
 
        if (buffer) {
                if (type == type_from_string(expected_type))
-                       ret = check_sha1_signature(sha1, buffer, size, expected_type);
+                       ret = check_sha1_signature(repl, buffer, size, expected_type);
                free(buffer);
        }
        return ret;
@@ -165,7 +166,7 @@ int main(int argc, char **argv)
        setup_git_directory();
 
        if (strbuf_read(&buf, 0, 4096) < 0) {
-               die("could not read from stdin");
+               die_errno("could not read from stdin");
        }
 
        /* Verify it for some basic sanity: it needs to start with
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);
-}
diff --git a/mozilla-sha1/sha1.c b/mozilla-sha1/sha1.c
deleted file mode 100644 (file)
index 95a4ebf..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is SHA 180-1 Reference Implementation (Compact version)
- *
- * The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research.  Portions created by Paul Kocher are
- * Copyright (C) 1995-9 by Cryptography Research, Inc.  All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- *     Paul Kocher
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above.  If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL.  If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
-
-#include "sha1.h"
-
-static void shaHashBlock(moz_SHA_CTX *ctx);
-
-void moz_SHA1_Init(moz_SHA_CTX *ctx) {
-  int i;
-
-  ctx->lenW = 0;
-  ctx->sizeHi = ctx->sizeLo = 0;
-
-  /* Initialize H with the magic constants (see FIPS180 for constants)
-   */
-  ctx->H[0] = 0x67452301;
-  ctx->H[1] = 0xefcdab89;
-  ctx->H[2] = 0x98badcfe;
-  ctx->H[3] = 0x10325476;
-  ctx->H[4] = 0xc3d2e1f0;
-
-  for (i = 0; i < 80; i++)
-    ctx->W[i] = 0;
-}
-
-
-void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *_dataIn, int len) {
-  const unsigned char *dataIn = _dataIn;
-  int i;
-
-  /* Read the data into W and process blocks as they get full
-   */
-  for (i = 0; i < len; i++) {
-    ctx->W[ctx->lenW / 4] <<= 8;
-    ctx->W[ctx->lenW / 4] |= (unsigned int)dataIn[i];
-    if ((++ctx->lenW) % 64 == 0) {
-      shaHashBlock(ctx);
-      ctx->lenW = 0;
-    }
-    ctx->sizeLo += 8;
-    ctx->sizeHi += (ctx->sizeLo < 8);
-  }
-}
-
-
-void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx) {
-  unsigned char pad0x80 = 0x80;
-  unsigned char pad0x00 = 0x00;
-  unsigned char padlen[8];
-  int i;
-
-  /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length
-   */
-  padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255);
-  padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255);
-  padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255);
-  padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255);
-  padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255);
-  padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
-  padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
-  padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
-  moz_SHA1_Update(ctx, &pad0x80, 1);
-  while (ctx->lenW != 56)
-    moz_SHA1_Update(ctx, &pad0x00, 1);
-  moz_SHA1_Update(ctx, padlen, 8);
-
-  /* Output hash
-   */
-  for (i = 0; i < 20; i++) {
-    hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24);
-    ctx->H[i / 4] <<= 8;
-  }
-
-  /*
-   *  Re-initialize the context (also zeroizes contents)
-   */
-  moz_SHA1_Init(ctx);
-}
-
-
-#define SHA_ROT(X,n) (((X) << (n)) | ((X) >> (32-(n))))
-
-static void shaHashBlock(moz_SHA_CTX *ctx) {
-  int t;
-  unsigned int A,B,C,D,E,TEMP;
-
-  for (t = 16; t <= 79; t++)
-    ctx->W[t] =
-      SHA_ROT(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1);
-
-  A = ctx->H[0];
-  B = ctx->H[1];
-  C = ctx->H[2];
-  D = ctx->H[3];
-  E = ctx->H[4];
-
-  for (t = 0; t <= 19; t++) {
-    TEMP = SHA_ROT(A,5) + (((C^D)&B)^D)     + E + ctx->W[t] + 0x5a827999;
-    E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
-  }
-  for (t = 20; t <= 39; t++) {
-    TEMP = SHA_ROT(A,5) + (B^C^D)           + E + ctx->W[t] + 0x6ed9eba1;
-    E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
-  }
-  for (t = 40; t <= 59; t++) {
-    TEMP = SHA_ROT(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdc;
-    E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
-  }
-  for (t = 60; t <= 79; t++) {
-    TEMP = SHA_ROT(A,5) + (B^C^D)           + E + ctx->W[t] + 0xca62c1d6;
-    E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
-  }
-
-  ctx->H[0] += A;
-  ctx->H[1] += B;
-  ctx->H[2] += C;
-  ctx->H[3] += D;
-  ctx->H[4] += E;
-}
diff --git a/mozilla-sha1/sha1.h b/mozilla-sha1/sha1.h
deleted file mode 100644 (file)
index aa48a46..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is SHA 180-1 Header File
- *
- * The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research.  Portions created by Paul Kocher are
- * Copyright (C) 1995-9 by Cryptography Research, Inc.  All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- *     Paul Kocher
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above.  If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL.  If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
-
-typedef struct {
-  unsigned int H[5];
-  unsigned int W[80];
-  int lenW;
-  unsigned int sizeHi,sizeLo;
-} moz_SHA_CTX;
-
-void moz_SHA1_Init(moz_SHA_CTX *ctx);
-void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *dataIn, int len);
-void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx);
-
-#define git_SHA_CTX    moz_SHA_CTX
-#define git_SHA1_Init  moz_SHA1_Init
-#define git_SHA1_Update        moz_SHA1_Update
-#define git_SHA1_Final moz_SHA1_Final
index 7e6a92c88e7b139ec03e0ff26e97e1559a06a220..fe8eaaf19f71b48d9acba83594d918fc4875f5c4 100644 (file)
--- a/object.c
+++ b/object.c
@@ -45,13 +45,14 @@ int type_from_string(const char *str)
 
 static unsigned int hash_obj(struct object *obj, unsigned int n)
 {
-       unsigned int hash = *(unsigned int *)obj->sha1;
+       unsigned int hash;
+       memcpy(&hash, obj->sha1, sizeof(unsigned int));
        return hash % 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++;
@@ -61,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)
@@ -187,17 +188,18 @@ struct object *parse_object(const unsigned char *sha1)
        unsigned long size;
        enum object_type type;
        int eaten;
-       void *buffer = read_sha1_file(sha1, &type, &size);
+       const unsigned char *repl;
+       void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
 
        if (buffer) {
                struct object *obj;
-               if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
+               if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) {
                        free(buffer);
-                       error("sha1 mismatch %s\n", sha1_to_hex(sha1));
+                       error("sha1 mismatch %s\n", sha1_to_hex(repl));
                        return NULL;
                }
 
-               obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
+               obj = parse_object_buffer(repl, type, size, buffer, &eaten);
                if (!eaten)
                        free(buffer);
                return obj;
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 48a12bc1352ad53fbc19ec8c5982a91673a098e1..69a7ab2e27d39ecabca5c0cc25c4e372caafb46c 100644 (file)
@@ -55,16 +55,15 @@ static inline struct llist_item *llist_item_get(void)
        } else {
                int i = 1;
                new = xmalloc(sizeof(struct llist_item) * BLKSIZE);
-               for(;i < BLKSIZE; i++) {
+               for (; i < BLKSIZE; i++)
                        llist_item_put(&new[i]);
-               }
        }
        return new;
 }
 
 static void llist_free(struct llist *list)
 {
-       while((list->back = list->front)) {
+       while ((list->back = list->front)) {
                list->front = list->front->next;
                llist_item_put(list->back);
        }
@@ -146,7 +145,7 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
                if (cmp > 0) { /* we insert before this entry */
                        return llist_insert(list, prev, sha1);
                }
-               if(!cmp) { /* already exists */
+               if (!cmp) { /* already exists */
                        return l;
                }
                prev = l;
@@ -168,7 +167,7 @@ redo_from_start:
                int cmp = hashcmp(l->sha1, sha1);
                if (cmp > 0) /* not in list, since sorted */
                        return prev;
-               if(!cmp) { /* found */
+               if (!cmp) { /* found */
                        if (prev == NULL) {
                                if (hint != NULL && hint != list->front) {
                                        /* we don't know the previous element */
@@ -218,7 +217,7 @@ static inline struct pack_list * pack_list_insert(struct pack_list **pl,
 static inline size_t pack_list_size(struct pack_list *pl)
 {
        size_t ret = 0;
-       while(pl) {
+       while (pl) {
                ret++;
                pl = pl->next;
        }
@@ -396,7 +395,7 @@ static size_t get_pack_redundancy(struct pack_list *pl)
                return 0;
 
        while ((subset = pl->next)) {
-               while(subset) {
+               while (subset) {
                        ret += sizeof_union(pl->pack, subset->pack);
                        subset = subset->next;
                }
@@ -427,7 +426,7 @@ static void minimize(struct pack_list **min)
 
        pl = local_packs;
        while (pl) {
-               if(pl->unique_objects->size)
+               if (pl->unique_objects->size)
                        pack_list_insert(&unique, pl);
                else
                        pack_list_insert(&non_unique, pl);
@@ -479,7 +478,7 @@ static void minimize(struct pack_list **min)
        *min = min_perm;
        /* add the unique packs to the list */
        pl = unique;
-       while(pl) {
+       while (pl) {
                pack_list_insert(min, pl);
                pl = pl->next;
        }
@@ -516,7 +515,7 @@ static void cmp_local_packs(void)
        struct pack_list *subset, *pl = local_packs;
 
        while ((subset = pl)) {
-               while((subset = subset->next))
+               while ((subset = subset->next))
                        cmp_two_packs(pl, subset);
                pl = pl->next;
        }
@@ -608,23 +607,23 @@ int main(int argc, char **argv)
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
-               if(!strcmp(arg, "--")) {
+               if (!strcmp(arg, "--")) {
                        i++;
                        break;
                }
-               if(!strcmp(arg, "--all")) {
+               if (!strcmp(arg, "--all")) {
                        load_all_packs = 1;
                        continue;
                }
-               if(!strcmp(arg, "--verbose")) {
+               if (!strcmp(arg, "--verbose")) {
                        verbose = 1;
                        continue;
                }
-               if(!strcmp(arg, "--alt-odb")) {
+               if (!strcmp(arg, "--alt-odb")) {
                        alt_odb = 1;
                        continue;
                }
-               if(*arg == '-')
+               if (*arg == '-')
                        usage(pack_redundant_usage);
                else
                        break;
index 2c76fb181f64e10c65517224b0c09cb648db81ac..7f43f8ac3398fb2dbe393f08811989b0a8d4e2dd 100644 (file)
@@ -66,7 +66,7 @@ static void prune_ref(struct ref_to_prune *r)
        struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
 
        if (lock) {
-               unlink(git_path("%s", r->name));
+               unlink_or_warn(git_path("%s", r->name));
                unlock_ref(lock);
        }
 }
@@ -93,8 +93,7 @@ int pack_refs(unsigned int flags)
                                       LOCK_DIE_ON_ERROR);
        cbdata.refs_file = fdopen(fd, "w");
        if (!cbdata.refs_file)
-               die("unable to create ref-pack file structure (%s)",
-                   strerror(errno));
+               die_errno("unable to create ref-pack file structure");
 
        /* perhaps other traits later as well */
        fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
@@ -103,7 +102,7 @@ int pack_refs(unsigned int flags)
        if (ferror(cbdata.refs_file))
                die("failed to write ref-pack file");
        if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file))
-               die("failed to write ref-pack file (%s)", strerror(errno));
+               die_errno("failed to write ref-pack file");
        /*
         * Since the lock file was fdopen()'ed and then fclose()'ed above,
         * assign -1 to the lock file descriptor so that commit_lock_file()
@@ -111,7 +110,7 @@ int pack_refs(unsigned int flags)
         */
        packed.fd = -1;
        if (commit_lock_file(&packed) < 0)
-               die("unable to overwrite old ref-pack file (%s)", strerror(errno));
+               die_errno("unable to overwrite old ref-pack file");
        if (cbdata.flags & PACK_REFS_PRUNE)
                prune_refs(cbdata.ref_to_prune);
        return 0;
index 1de53c8934c03b46604e94cbf4237ad5ffc57f83..77a0465be6f6a79814aa3c009612736770b342a1 100644 (file)
@@ -149,8 +149,7 @@ void discard_revindex(void)
        if (pack_revindex_hashsz) {
                int i;
                for (i = 0; i < pack_revindex_hashsz; i++)
-                       if (pack_revindex[i].revindex)
-                               free(pack_revindex[i].revindex);
+                       free(pack_revindex[i].revindex);
                free(pack_revindex);
                pack_revindex_hashsz = 0;
        }
index 7053538f4cf44e15a788ab46dfb680ee85ce4fc2..741efcd93bd2a2c105bfe2d771d32857ce2ae816 100644 (file)
@@ -51,7 +51,7 @@ char *write_idx_file(char *index_name, struct pack_idx_entry **objects,
                fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
        }
        if (fd < 0)
-               die("unable to create %s: %s", index_name, strerror(errno));
+               die_errno("unable to create '%s'", index_name);
        f = sha1fd(fd, index_name);
 
        /* if last object's offset is >= 2^31 we should use index V2 */
@@ -174,11 +174,11 @@ void fixup_pack_header_footer(int pack_fd,
        git_SHA1_Init(&new_sha1_ctx);
 
        if (lseek(pack_fd, 0, SEEK_SET) != 0)
-               die("Failed seeking to start of %s: %s", pack_name, strerror(errno));
+               die_errno("Failed seeking to start of '%s'", pack_name);
        if (read_in_full(pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
-               die("Unable to reread header of %s: %s", pack_name, strerror(errno));
+               die_errno("Unable to reread header of '%s'", pack_name);
        if (lseek(pack_fd, 0, SEEK_SET) != 0)
-               die("Failed seeking to start of %s: %s", pack_name, strerror(errno));
+               die_errno("Failed seeking to start of '%s'", pack_name);
        git_SHA1_Update(&old_sha1_ctx, &hdr, sizeof(hdr));
        hdr.hdr_entries = htonl(object_count);
        git_SHA1_Update(&new_sha1_ctx, &hdr, sizeof(hdr));
@@ -195,7 +195,7 @@ void fixup_pack_header_footer(int pack_fd,
                if (!n)
                        break;
                if (n < 0)
-                       die("Failed to checksum %s: %s", pack_name, strerror(errno));
+                       die_errno("Failed to checksum '%s'", pack_name);
                git_SHA1_Update(&new_sha1_ctx, buf, n);
 
                aligned_sz -= n;
diff --git a/pager.c b/pager.c
index 4921843577e42b774457a61277b9bc3441d3ab6b..86facec7b417b26a7dbd9b3c1338149fcecd5588 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -9,7 +9,7 @@
 
 static int spawned_pager;
 
-#ifndef __MINGW32__
+#ifndef WIN32
 static void pager_preexec(void)
 {
        /*
@@ -21,8 +21,6 @@ static void pager_preexec(void)
        FD_ZERO(&in);
        FD_SET(0, &in);
        select(1, &in, NULL, &in, NULL);
-
-       setenv("LESS", "FRSX", 0);
 }
 #endif
 
@@ -70,7 +68,11 @@ void setup_pager(void)
        pager_argv[2] = pager;
        pager_process.argv = pager_argv;
        pager_process.in = -1;
-#ifndef __MINGW32__
+       if (!getenv("LESS")) {
+               static const char *env[] = { "LESS=FRSX", NULL };
+               pager_process.env = env;
+       }
+#ifndef WIN32
        pager_process.preexec_cb = pager_preexec;
 #endif
        if (start_command(&pager_process))
index cf71bcffd2e1e9aeacb44df488b0825f09d54255..f5594114ede8a50090c8b97987b52a660235fa56 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;
 }
@@ -178,6 +230,9 @@ is_abbreviated:
                                abbrev_flags = flags;
                                continue;
                        }
+                       /* negation allowed? */
+                       if (options->flags & PARSE_OPT_NONEG)
+                               continue;
                        /* negated and abbreviated very much? */
                        if (!prefixcmp("no-", arg)) {
                                flags |= OPT_UNSET;
@@ -215,6 +270,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 +309,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 +356,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 +365,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 +440,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,10 +464,24 @@ 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
 
-int usage_with_options_internal(const char * const *usagestr,
+static int usage_with_options_internal(const char * const *usagestr,
                                const struct option *opts, int full)
 {
        if (!usagestr)
@@ -397,51 +514,23 @@ 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 && !(opts->flags & PARSE_OPT_NEGHELP)) {
+                       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);
+                       pos += fprintf(stderr, "--%s%s",
+                               (opts->flags & PARSE_OPT_NEGHELP) ?  "no-" : "",
+                               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;
@@ -463,6 +552,14 @@ void usage_with_options(const char * const *usagestr,
        exit(129);
 }
 
+void usage_msg_opt(const char *msg,
+                  const char * const *usagestr,
+                  const struct option *options)
+{
+       fprintf(stderr, "%s\n\n", msg);
+       usage_with_options(usagestr, options);
+}
+
 int parse_options_usage(const char * const *usagestr,
                        const struct option *opts)
 {
@@ -536,15 +633,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..f295a2cf858f4dbebc62e956878544ffdb7fbe58 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,9 @@ 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,
+       PARSE_OPT_NEGHELP = 128,
 };
 
 struct option;
@@ -62,10 +68,22 @@ 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.
+ *   PARSE_OPT_NEGHELP: says that the long option should always be shown with
+ *                             the --no prefix in the usage message. Sometimes
+ *                             useful for users of OPTION_NEGBIT.
  *
  * `callback`::
  *   pointer to the callback to use for OPTION_CALLBACK.
@@ -90,31 +108,47 @@ 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);
 
 extern NORETURN void usage_with_options(const char * const *usagestr,
                                         const struct option *options);
 
+extern NORETURN void usage_msg_opt(const char *msg,
+                                  const char * const *usagestr,
+                                  const struct option *options);
+
 /*----- incremental advanced APIs -----*/
 
 enum {
@@ -134,13 +168,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 +204,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..e02e13bd4eb2a92626c2d6f9cbf264abb15de9c5 100644 (file)
@@ -2,7 +2,7 @@
  * patch-delta.c:
  * recreate a buffer from a source and the delta produced by diff-delta.c
  *
- * (C) 2005 Nicolas Pitre <nico@cam.org>
+ * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -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 e3dd1a5547c471208c445d77263ee46e64b37451..4ab21d61b808d2e396419fb41419bcea7ab3cde4 100644 (file)
@@ -29,11 +29,11 @@ $(makfile): ../GIT-CFLAGS Makefile
        '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \
        echo '  cp private-Error.pm blib/lib/Error.pm' >> $@
        echo install: >> $@
-       echo '  mkdir -p "$(instdir_SQ)"' >> $@
-       echo '  $(RM) "$(instdir_SQ)/Git.pm"; cp Git.pm "$(instdir_SQ)"' >> $@
-       echo '  $(RM) "$(instdir_SQ)/Error.pm"' >> $@
+       echo '  mkdir -p "$$(DESTDIR)$(instdir_SQ)"' >> $@
+       echo '  $(RM) "$$(DESTDIR)$(instdir_SQ)/Git.pm"; cp Git.pm "$$(DESTDIR)$(instdir_SQ)"' >> $@
+       echo '  $(RM) "$$(DESTDIR)$(instdir_SQ)/Error.pm"' >> $@
        '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \
-       echo '  cp private-Error.pm "$(instdir_SQ)/Error.pm"' >> $@
+       echo '  cp private-Error.pm "$$(DESTDIR)$(instdir_SQ)/Error.pm"' >> $@
        echo instlibdir: >> $@
        echo '  echo $(instdir_SQ)' >> $@
 else
index 320253eb8e91eb1d914aa4e34f7d3af4649b9b39..0b9deca2cc6ef77897a23b2096a7acdd577c2482 100644 (file)
@@ -5,6 +5,14 @@ sub MY::postamble {
 instlibdir:
        @echo '$(INSTALLSITELIB)'
 
+ifneq (,$(DESTDIR))
+ifeq (0,$(shell expr '$(MM_VERSION)' '>' 6.10))
+$(error ExtUtils::MakeMaker version "$(MM_VERSION)" is older than 6.11 and so \
+       is likely incompatible with the DESTDIR mechanism.  Try setting \
+       NO_PERL_MAKEMAKER=1 instead)
+endif
+endif
+
 MAKE_FRAG
 }
 
index f5d00863a6234c16db33637d19fefd2014780e87..b691abebd79b40f096b28b5dbcb433f01bc0e949 100644 (file)
@@ -28,7 +28,7 @@ ssize_t safe_write(int fd, const void *buf, ssize_t n)
                }
                if (!ret)
                        die("write error (disk full?)");
-               die("write error (%s)", strerror(errno));
+               die_errno("write error");
        }
        return nn;
 }
@@ -67,7 +67,7 @@ static void safe_read(int fd, void *buffer, unsigned size)
 {
        ssize_t ret = read_in_full(fd, buffer, size);
        if (ret < 0)
-               die("read error (%s)", strerror(errno));
+               die_errno("read error");
        else if (ret < size)
                die("The remote end hung up unexpectedly");
 }
index 88edc5f8a9d5384e19426e6adb40e08b34d3adf2..92899333c2d8edbed71fdd3a43e19f25a10e5b03 100644 (file)
@@ -34,7 +34,9 @@ static void *preload_thread(void *_data)
        struct thread_data *p = _data;
        struct index_state *index = p->index;
        struct cache_entry **cep = index->cache + p->offset;
+       struct cache_def cache;
 
+       memset(&cache, 0, sizeof(cache));
        nr = p->nr;
        if (nr + p->offset > index->cache_nr)
                nr = index->cache_nr - p->offset;
@@ -49,6 +51,8 @@ static void *preload_thread(void *_data)
                        continue;
                if (!ce_path_match(ce, p->pathspec))
                        continue;
+               if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
+                       continue;
                if (lstat(ce->name, &st))
                        continue;
                if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
@@ -72,7 +76,7 @@ static void preload_index(struct index_state *index, const char **pathspec)
        if (threads > MAX_PARALLEL)
                threads = MAX_PARALLEL;
        offset = 0;
-       work = (index->cache_nr + threads - 1) / threads;
+       work = DIV_ROUND_UP(index->cache_nr, threads);
        for (i = 0; i < threads; i++) {
                struct thread_data *p = data+i;
                p->index = index;
index a0ef356558f4cdb148010f1b47dbd3fcc363d8ba..f5983f8baa98b69338707b4220e64012ef4e5d11 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -86,6 +86,18 @@ int non_ascii(int ch)
        return !isascii(ch) || ch == '\033';
 }
 
+int has_non_ascii(const char *s)
+{
+       int ch;
+       if (!s)
+               return 0;
+       while ((ch = *s++) != '\0') {
+               if (non_ascii(ch))
+                       return 1;
+       }
+       return 0;
+}
+
 static int is_rfc2047_special(char ch)
 {
        return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
@@ -284,7 +296,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;
@@ -571,7 +583,7 @@ static void format_decoration(struct strbuf *sb, const struct commit *commit)
        struct name_decoration *d;
        const char *prefix = " (";
 
-       load_ref_decorations();
+       load_ref_decorations(DECORATE_SHORT_REFS);
        d = lookup_decoration(&name_decoration, &commit->object);
        while (d) {
                strbuf_addstr(sb, prefix);
@@ -881,7 +893,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);
index 621c34edc2a3a6a9bceb0f708ed5b0df05145fb5..3971f49f4ddaa774806bd1717379a1edd22ba17c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Simple text-based progress display module for GIT
  *
- * Copyright (c) 2007 by Nicolas Pitre <nico@cam.org>
+ * Copyright (c) 2007 by Nicolas Pitre <nico@fluxnic.net>
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -131,7 +131,13 @@ static void throughput_string(struct throughput *tp, off_t total,
        } else {
                l -= snprintf(tp->display, l, ", %u bytes", (int)total);
        }
-       if (rate)
+
+       if (rate > 1 << 10) {
+               int x = rate + 5;  /* for rounding */
+               snprintf(tp->display + sizeof(tp->display) - l, l,
+                        " | %u.%2.2u MiB/s",
+                        x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
+       } else if (rate)
                snprintf(tp->display + sizeof(tp->display) - l, l,
                         " | %u KiB/s", rate);
 }
diff --git a/quote.c b/quote.c
index 7a49fcf69671646a0d3ba6de6478cfc6767c31fe..848d174cc56be32483ba6c73f9b06bc398ed6437 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -272,8 +272,8 @@ void write_name_quoted(const char *name, FILE *fp, int terminator)
        fputc(terminator, fp);
 }
 
-extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
-                                 const char *name, FILE *fp, int terminator)
+void write_name_quotedpfx(const char *pfx, size_t pfxlen,
+                         const char *name, FILE *fp, int terminator)
 {
        int needquote = 0;
 
index 3f587110cb9d7be1890b7db68a0bdac35d48cd35..1bbaf1cffba3058667787fc4a267b87e21a6cad3 100644 (file)
@@ -638,7 +638,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int flags)
 {
        struct stat st;
        if (lstat(path, &st))
-               die("%s: unable to stat (%s)", path, strerror(errno));
+               die_errno("unable to stat '%s'", path);
        return add_to_index(istate, path, &st, flags);
 }
 
@@ -1065,7 +1065,18 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
        return updated;
 }
 
-int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, char *seen)
+static void show_file(const char * fmt, const char * name, int in_porcelain,
+                     int * first, char *header_msg)
+{
+       if (in_porcelain && *first && header_msg) {
+               printf("%s\n", header_msg);
+               *first=0;
+       }
+       printf(fmt, name);
+}
+
+int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
+                 char *seen, char *header_msg)
 {
        int i;
        int has_errors = 0;
@@ -1074,11 +1085,14 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
        int quiet = (flags & REFRESH_QUIET) != 0;
        int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
        int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
+       int first = 1;
+       int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
        unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
-       const char *needs_update_message;
+       const char *needs_update_fmt;
+       const char *needs_merge_fmt;
 
-       needs_update_message = ((flags & REFRESH_SAY_CHANGED)
-                               ? "locally modified" : "needs update");
+       needs_update_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
+       needs_merge_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce, *new;
                int cache_errno = 0;
@@ -1094,7 +1108,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
                        i--;
                        if (allow_unmerged)
                                continue;
-                       printf("%s: needs merge\n", ce->name);
+                       show_file(needs_merge_fmt, ce->name, in_porcelain, &first, header_msg);
                        has_errors = 1;
                        continue;
                }
@@ -1117,7 +1131,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
                        }
                        if (quiet)
                                continue;
-                       printf("%s: %s\n", ce->name, needs_update_message);
+                       show_file(needs_update_fmt, ce->name, in_porcelain, &first, header_msg);
                        has_errors = 1;
                        continue;
                }
@@ -1251,11 +1265,11 @@ int read_index_from(struct index_state *istate, const char *path)
        if (fd < 0) {
                if (errno == ENOENT)
                        return 0;
-               die("index file open failed (%s)", strerror(errno));
+               die_errno("index file open failed");
        }
 
        if (fstat(fd, &st))
-               die("cannot stat the open index (%s)", strerror(errno));
+               die_errno("cannot stat the open index");
 
        errno = EINVAL;
        mmap_size = xsize_t(st.st_size);
@@ -1265,7 +1279,7 @@ int read_index_from(struct index_state *istate, const char *path)
        mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        close(fd);
        if (mmap == MAP_FAILED)
-               die("unable to map index file");
+               die_errno("unable to map index file");
 
        hdr = mmap;
        if (verify_hdr(hdr, mmap_size) < 0)
diff --git a/refs.c b/refs.c
index e65a3b4c4ef57863a1055108d2598777cabc2c8d..808f56bb27b6ece616f400154fe98c9cef12f8ba 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -531,9 +531,10 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 {
        if (strncmp(base, entry->name, trim))
                return 0;
+       /* Is this a "negative ref" that represents a deleted ref? */
+       if (is_null_sha1(entry->sha1))
+               return 0;
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
-               if (is_null_sha1(entry->sha1))
-                       return 0;
                if (!has_sha1_file(entry->sha1)) {
                        error("%s does not point to a valid object!", entry->name);
                        return 0;
@@ -667,6 +668,11 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data)
        return for_each_ref_in("refs/remotes/", fn, cb_data);
 }
 
+int for_each_replace_ref(each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
+}
+
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
        return do_for_each_ref("refs/", fn, 0,
@@ -682,12 +688,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 +757,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 :
@@ -820,7 +826,7 @@ static int remove_empty_directories(const char *file)
        strbuf_init(&path, 20);
        strbuf_addstr(&path, file);
 
-       result = remove_dir_recursively(&path, 1);
+       result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
 
        strbuf_release(&path);
 
@@ -893,8 +899,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
         * name is a proper prefix of our refname.
         */
        if (missing &&
-            !is_refname_available(ref, NULL, get_packed_refs(), 0))
+            !is_refname_available(ref, NULL, get_packed_refs(), 0)) {
+               last_errno = ENOTDIR;
                goto error_return;
+       }
 
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
@@ -964,8 +972,10 @@ static int repack_without_ref(const char *refname)
        if (!found)
                return 0;
        fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
-       if (fd < 0)
+       if (fd < 0) {
+               unable_to_lock_error(git_path("packed-refs"), errno);
                return error("cannot delete '%s' from packed refs", refname);
+       }
 
        for (list = packed_ref_list; list; list = list->next) {
                char line[PATH_MAX + 100];
@@ -1002,12 +1012,10 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
                } else {
                        path = git_path("%s", refname);
                }
-               err = unlink(path);
-               if (err && errno != ENOENT) {
+               err = unlink_or_warn(path);
+               if (err && errno != ENOENT)
                        ret = 1;
-                       error("unlink(%s) failed: %s",
-                             path, strerror(errno));
-               }
+
                if (!(delopt & REF_NODEREF))
                        lock->lk->filename[i] = '.';
        }
@@ -1017,10 +1025,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
         */
        ret |= repack_without_ref(refname);
 
-       err = unlink(git_path("logs/%s", lock->ref_name));
-       if (err && errno != ENOENT)
-               warning("unlink(%s) failed: %s",
-                       git_path("logs/%s", lock->ref_name), strerror(errno));
+       unlink_or_warn(git_path("logs/%s", lock->ref_name));
        invalidate_cached_refs();
        unlock_ref(lock);
        return ret;
@@ -1381,7 +1386,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
        if (adjust_shared_perm(git_HEAD)) {
                error("Unable to fix permissions on %s", lockpath);
        error_unlink_return:
-               unlink(lockpath);
+               unlink_or_warn(lockpath);
        error_free_return:
                free(git_HEAD);
                return -1;
@@ -1421,7 +1426,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
        logfile = git_path("logs/%s", ref);
        logfd = open(logfile, O_RDONLY, 0);
        if (logfd < 0)
-               die("Unable to read log %s: %s", logfile, strerror(errno));
+               die_errno("Unable to read log '%s'", logfile);
        fstat(logfd, &st);
        if (!st.st_size)
                die("Log %s is empty.", logfile);
@@ -1528,8 +1533,10 @@ int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs,
                if (fstat(fileno(logfp), &statbuf) ||
                    statbuf.st_size < ofs ||
                    fseek(logfp, -ofs, SEEK_END) ||
-                   fgets(buf, sizeof(buf), logfp))
+                   fgets(buf, sizeof(buf), logfp)) {
+                       fclose(logfp);
                        return -1;
+               }
        }
 
        while (fgets(buf, sizeof(buf), logfp)) {
diff --git a/refs.h b/refs.h
index 29d17a48e4a2923bc72337deb1ef64cf7b467381..777b5b7ca6c08ef63f44abbf0236fc180cb9c6d1 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -24,6 +24,7 @@ extern int for_each_ref_in(const char *, each_ref_fn, void *);
 extern int for_each_tag_ref(each_ref_fn, void *);
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
+extern int for_each_replace_ref(each_ref_fn, void *);
 
 /* can be used to learn about broken ref and symref */
 extern int for_each_rawref(each_ref_fn, void *);
@@ -80,7 +81,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 **/
diff --git a/remote-curl.c b/remote-curl.c
new file mode 100644 (file)
index 0000000..2faf1c6
--- /dev/null
@@ -0,0 +1,141 @@
+#include "cache.h"
+#include "remote.h"
+#include "strbuf.h"
+#include "walker.h"
+#include "http.h"
+#include "exec_cmd.h"
+
+static struct ref *get_refs(struct walker *walker, const char *url)
+{
+       struct strbuf buffer = STRBUF_INIT;
+       char *data, *start, *mid;
+       char *ref_name;
+       char *refs_url;
+       int i = 0;
+       int http_ret;
+
+       struct ref *refs = NULL;
+       struct ref *ref = NULL;
+       struct ref *last_ref = NULL;
+
+       refs_url = xmalloc(strlen(url) + 11);
+       sprintf(refs_url, "%s/info/refs", url);
+
+       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;
+       start = NULL;
+       mid = data;
+       while (i < buffer.len) {
+               if (!start) {
+                       start = &data[i];
+               }
+               if (data[i] == '\t')
+                       mid = &data[i];
+               if (data[i] == '\n') {
+                       data[i] = 0;
+                       ref_name = mid + 1;
+                       ref = xmalloc(sizeof(struct ref) +
+                                     strlen(ref_name) + 1);
+                       memset(ref, 0, sizeof(struct ref));
+                       strcpy(ref->name, ref_name);
+                       get_sha1_hex(start, ref->old_sha1);
+                       if (!refs)
+                               refs = ref;
+                       if (last_ref)
+                               last_ref->next = ref;
+                       last_ref = ref;
+                       start = NULL;
+               }
+               i++;
+       }
+
+       strbuf_release(&buffer);
+
+       ref = alloc_ref("HEAD");
+       if (!walker->fetch_ref(walker, ref) &&
+           !resolve_remote_symref(ref, refs)) {
+               ref->next = refs;
+               refs = ref;
+       } else {
+               free(ref);
+       }
+
+       strbuf_release(&buffer);
+       free(refs_url);
+       return refs;
+}
+
+int main(int argc, const char **argv)
+{
+       struct remote *remote;
+       struct strbuf buf = STRBUF_INIT;
+       const char *url;
+       struct walker *walker = NULL;
+
+       git_extract_argv0_path(argv[0]);
+       setup_git_directory();
+       if (argc < 2) {
+               fprintf(stderr, "Remote needed\n");
+               return 1;
+       }
+
+       remote = remote_get(argv[1]);
+
+       if (argc > 2) {
+               url = argv[2];
+       } else {
+               url = remote->url[0];
+       }
+
+       do {
+               if (strbuf_getline(&buf, stdin, '\n') == EOF)
+                       break;
+               if (!prefixcmp(buf.buf, "fetch ")) {
+                       char *obj = buf.buf + strlen("fetch ");
+                       if (!walker)
+                               walker = get_http_walker(url, remote);
+                       walker->get_all = 1;
+                       walker->get_tree = 1;
+                       walker->get_history = 1;
+                       walker->get_verbosely = 0;
+                       walker->get_recover = 0;
+                       if (walker_fetch(walker, 1, &obj, NULL, NULL))
+                               die("Fetch failed.");
+                       printf("\n");
+                       fflush(stdout);
+               } else if (!strcmp(buf.buf, "list")) {
+                       struct ref *refs;
+                       struct ref *posn;
+                       if (!walker)
+                               walker = get_http_walker(url, remote);
+                       refs = get_refs(walker, url);
+                       for (posn = refs; posn; posn = posn->next) {
+                               if (posn->symref)
+                                       printf("@%s %s\n", posn->symref, posn->name);
+                               else
+                                       printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
+                       }
+                       printf("\n");
+                       fflush(stdout);
+               } else if (!strcmp(buf.buf, "capabilities")) {
+                       printf("fetch\n");
+                       printf("\n");
+                       fflush(stdout);
+               } else {
+                       return 1;
+               }
+               strbuf_reset(&buf);
+       } while (1);
+       return 0;
+}
index d66e2f3c93dc72a7112ce101278ae937cc914320..73d33f2584b061085fcaa0958c26b1c3d4665904 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -28,6 +28,11 @@ struct rewrite {
        int instead_of_nr;
        int instead_of_alloc;
 };
+struct rewrites {
+       struct rewrite **rewrite;
+       int rewrite_alloc;
+       int rewrite_nr;
+};
 
 static struct remote **remotes;
 static int remotes_alloc;
@@ -41,14 +46,13 @@ static struct branch *current_branch;
 static const char *default_remote_name;
 static int explicit_default_remote_name;
 
-static struct rewrite **rewrite;
-static int rewrite_alloc;
-static int rewrite_nr;
+static struct rewrites rewrites;
+static struct rewrites rewrites_push;
 
 #define BUF_SIZE (2048)
 static char buffer[BUF_SIZE];
 
-static const char *alias_url(const char *url)
+static const char *alias_url(const char *url, struct rewrites *r)
 {
        int i, j;
        char *ret;
@@ -57,14 +61,14 @@ static const char *alias_url(const char *url)
 
        longest = NULL;
        longest_i = -1;
-       for (i = 0; i < rewrite_nr; i++) {
-               if (!rewrite[i])
+       for (i = 0; i < r->rewrite_nr; i++) {
+               if (!r->rewrite[i])
                        continue;
-               for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
-                       if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+               for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
+                       if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
                            (!longest ||
-                            longest->len < rewrite[i]->instead_of[j].len)) {
-                               longest = &(rewrite[i]->instead_of[j]);
+                            longest->len < r->rewrite[i]->instead_of[j].len)) {
+                               longest = &(r->rewrite[i]->instead_of[j]);
                                longest_i = i;
                        }
                }
@@ -72,10 +76,10 @@ static const char *alias_url(const char *url)
        if (!longest)
                return url;
 
-       ret = xmalloc(rewrite[longest_i]->baselen +
+       ret = xmalloc(r->rewrite[longest_i]->baselen +
                     (strlen(url) - longest->len) + 1);
-       strcpy(ret, rewrite[longest_i]->base);
-       strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+       strcpy(ret, r->rewrite[longest_i]->base);
+       strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
        return ret;
 }
 
@@ -101,9 +105,23 @@ static void add_url(struct remote *remote, const char *url)
        remote->url[remote->url_nr++] = url;
 }
 
+static void add_pushurl(struct remote *remote, const char *pushurl)
+{
+       ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
+       remote->pushurl[remote->pushurl_nr++] = pushurl;
+}
+
+static void add_pushurl_alias(struct remote *remote, const char *url)
+{
+       const char *pushurl = alias_url(url, &rewrites_push);
+       if (pushurl != url)
+               add_pushurl(remote, pushurl);
+}
+
 static void add_url_alias(struct remote *remote, const char *url)
 {
-       add_url(remote, alias_url(url));
+       add_url(remote, alias_url(url, &rewrites));
+       add_pushurl_alias(remote, url);
 }
 
 static struct remote *make_remote(const char *name, int len)
@@ -163,22 +181,22 @@ static struct branch *make_branch(const char *name, int len)
        return ret;
 }
 
-static struct rewrite *make_rewrite(const char *base, int len)
+static struct rewrite *make_rewrite(struct rewrites *r, const char *base, int len)
 {
        struct rewrite *ret;
        int i;
 
-       for (i = 0; i < rewrite_nr; i++) {
+       for (i = 0; i < r->rewrite_nr; i++) {
                if (len
-                   ? (len == rewrite[i]->baselen &&
-                      !strncmp(base, rewrite[i]->base, len))
-                   : !strcmp(base, rewrite[i]->base))
-                       return rewrite[i];
+                   ? (len == r->rewrite[i]->baselen &&
+                      !strncmp(base, r->rewrite[i]->base, len))
+                   : !strcmp(base, r->rewrite[i]->base))
+                       return r->rewrite[i];
        }
 
-       ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+       ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
        ret = xcalloc(1, sizeof(struct rewrite));
-       rewrite[rewrite_nr++] = ret;
+       r->rewrite[r->rewrite_nr++] = ret;
        if (len) {
                ret->base = xstrndup(base, len);
                ret->baselen = len;
@@ -301,7 +319,7 @@ static void read_branches_file(struct remote *remote)
                strbuf_addstr(&branch, "HEAD:");
        }
        add_url_alias(remote, p);
-       add_fetch_refspec(remote, strbuf_detach(&branch, 0));
+       add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
        /*
         * Cogito compatible push: push current HEAD to remote #branch
         * (master if missing)
@@ -312,7 +330,7 @@ static void read_branches_file(struct remote *remote)
                strbuf_addf(&branch, ":refs/heads/%s", frag);
        else
                strbuf_addstr(&branch, ":refs/heads/master");
-       add_push_refspec(remote, strbuf_detach(&branch, 0));
+       add_push_refspec(remote, strbuf_detach(&branch, NULL));
        remote->fetch_tags = 1; /* always auto-follow */
 }
 
@@ -349,8 +367,13 @@ static int handle_config(const char *key, const char *value, void *cb)
                subkey = strrchr(name, '.');
                if (!subkey)
                        return 0;
-               rewrite = make_rewrite(name, subkey - name);
                if (!strcmp(subkey, ".insteadof")) {
+                       rewrite = make_rewrite(&rewrites, name, subkey - name);
+                       if (!value)
+                               return config_error_nonbool(key);
+                       add_instead_of(rewrite, xstrdup(value));
+               } else if (!strcmp(subkey, ".pushinsteadof")) {
+                       rewrite = make_rewrite(&rewrites_push, name, subkey - name);
                        if (!value)
                                return config_error_nonbool(key);
                        add_instead_of(rewrite, xstrdup(value));
@@ -379,6 +402,11 @@ static int handle_config(const char *key, const char *value, void *cb)
                if (git_config_string(&v, key, value))
                        return -1;
                add_url(remote, v);
+       } else if (!strcmp(subkey, ".pushurl")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_pushurl(remote, v);
        } else if (!strcmp(subkey, ".push")) {
                const char *v;
                if (git_config_string(&v, key, value))
@@ -419,10 +447,17 @@ static void alias_all_urls(void)
 {
        int i, j;
        for (i = 0; i < remotes_nr; i++) {
+               int add_pushurl_aliases;
                if (!remotes[i])
                        continue;
+               for (j = 0; j < remotes[i]->pushurl_nr; j++) {
+                       remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
+               }
+               add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
                for (j = 0; j < remotes[i]->url_nr; j++) {
-                       remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
+                       if (add_pushurl_aliases)
+                               add_pushurl_alias(remotes[i], remotes[i]->url[j]);
+                       remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
                }
        }
 }
@@ -1024,7 +1059,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        case 0:
                if (!memcmp(dst_value, "refs/", 5))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
-               else if((dst_guess = guess_ref(dst_value, matched_src)))
+               else if ((dst_guess = guess_ref(dst_value, matched_src)))
                        matched_dst = make_linked_ref(dst_guess, dst_tail);
                else
                        error("unable to push to unqualified destination: %s\n"
@@ -1085,26 +1120,35 @@ 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;
        int send_all = flags & MATCH_REFS_ALL;
        int send_mirror = flags & MATCH_REFS_MIRROR;
        int errs;
-       static const char *default_refspec[] = { ":", 0 };
+       static const char *default_refspec[] = { ":", NULL };
+       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 +1178,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 +1194,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);
@@ -1254,7 +1298,7 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 
 static struct ref *get_local_ref(const char *name)
 {
-       if (!name)
+       if (!name || name[0] == '\0')
                return NULL;
 
        if (!prefixcmp(name, "refs/"))
@@ -1399,13 +1443,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..5db842087da081abbad96c8f9eed5829140714d2 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -15,6 +15,10 @@ struct remote {
        int url_nr;
        int url_alloc;
 
+       const char **pushurl;
+       int pushurl_nr;
+       int pushurl_alloc;
+
        const char **push_refspec;
        struct refspec *push;
        int push_refspec_nr;
@@ -85,7 +89,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);
 
 /*
diff --git a/replace_object.c b/replace_object.c
new file mode 100644 (file)
index 0000000..eb59604
--- /dev/null
@@ -0,0 +1,114 @@
+#include "cache.h"
+#include "sha1-lookup.h"
+#include "refs.h"
+
+static struct replace_object {
+       unsigned char sha1[2][20];
+} **replace_object;
+
+static int replace_object_alloc, replace_object_nr;
+
+static const unsigned char *replace_sha1_access(size_t index, void *table)
+{
+       struct replace_object **replace = table;
+       return replace[index]->sha1[0];
+}
+
+static int replace_object_pos(const unsigned char *sha1)
+{
+       return sha1_pos(sha1, replace_object, replace_object_nr,
+                       replace_sha1_access);
+}
+
+static int register_replace_object(struct replace_object *replace,
+                                  int ignore_dups)
+{
+       int pos = replace_object_pos(replace->sha1[0]);
+
+       if (0 <= pos) {
+               if (ignore_dups)
+                       free(replace);
+               else {
+                       free(replace_object[pos]);
+                       replace_object[pos] = replace;
+               }
+               return 1;
+       }
+       pos = -pos - 1;
+       if (replace_object_alloc <= ++replace_object_nr) {
+               replace_object_alloc = alloc_nr(replace_object_alloc);
+               replace_object = xrealloc(replace_object,
+                                         sizeof(*replace_object) *
+                                         replace_object_alloc);
+       }
+       if (pos < replace_object_nr)
+               memmove(replace_object + pos + 1,
+                       replace_object + pos,
+                       (replace_object_nr - pos - 1) *
+                       sizeof(*replace_object));
+       replace_object[pos] = replace;
+       return 0;
+}
+
+static int register_replace_ref(const char *refname,
+                               const unsigned char *sha1,
+                               int flag, void *cb_data)
+{
+       /* Get sha1 from refname */
+       const char *slash = strrchr(refname, '/');
+       const char *hash = slash ? slash + 1 : refname;
+       struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
+
+       if (strlen(hash) != 40 || get_sha1_hex(hash, repl_obj->sha1[0])) {
+               free(repl_obj);
+               warning("bad replace ref name: %s", refname);
+               return 0;
+       }
+
+       /* Copy sha1 from the read ref */
+       hashcpy(repl_obj->sha1[1], sha1);
+
+       /* Register new object */
+       if (register_replace_object(repl_obj, 1))
+               die("duplicate replace ref: %s", refname);
+
+       return 0;
+}
+
+static void prepare_replace_object(void)
+{
+       static int replace_object_prepared;
+
+       if (replace_object_prepared)
+               return;
+
+       for_each_replace_ref(register_replace_ref, NULL);
+       replace_object_prepared = 1;
+}
+
+/* We allow "recursive" replacement. Only within reason, though */
+#define MAXREPLACEDEPTH 5
+
+const unsigned char *lookup_replace_object(const unsigned char *sha1)
+{
+       int pos, depth = MAXREPLACEDEPTH;
+       const unsigned char *cur = sha1;
+
+       if (!read_replace_refs)
+               return sha1;
+
+       prepare_replace_object();
+
+       /* Try to recursively replace the object */
+       do {
+               if (--depth < 0)
+                       die("replace depth too high for object %s",
+                           sha1_to_hex(sha1));
+
+               pos = replace_object_pos(cur);
+               if (0 <= pos)
+                       cur = replace_object[pos]->sha1[1];
+       } while (0 <= pos);
+
+       return cur;
+}
index 713c6e16ac536eaa667cc9f22768670be129483c..29f95f657d04300e375d007e9d01832b89311602 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -61,7 +61,7 @@ static int write_rr(struct string_list *rr, int out_fd)
                path = rr->items[i].string;
                length = strlen(path) + 1;
                if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
-                   write_in_full(out_fd, "\t", 1) != 1 ||
+                   write_str_in_full(out_fd, "\t") != 1 ||
                    write_in_full(out_fd, path, length) != length)
                        die("unable to write rerere record");
        }
@@ -173,7 +173,7 @@ static int handle_file(const char *path,
                git_SHA1_Final(sha1, &ctx);
        if (hunk != RR_CONTEXT) {
                if (output)
-                       unlink(output);
+                       unlink_or_warn(output);
                return error("Could not parse conflict hunks in %s", path);
        }
        if (wrerror)
index 18b7ebbbd599417462b5c4275c2581b91ebeca6a..9fc4e8d3818f29261b1963f4995bc36f8af31179 100644 (file)
@@ -133,7 +133,7 @@ void mark_parents_uninteresting(struct commit *commit)
 static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
 {
        if (revs->no_walk && (obj->flags & UNINTERESTING))
-               die("object ranges do not make sense when not walking revisions");
+               revs->no_walk = 0;
        if (revs->reflog_info && obj->type == OBJ_COMMIT &&
                        add_reflog_for_walk(revs->reflog_info,
                                (struct commit *)obj, name))
@@ -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;
@@ -1061,7 +1052,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->simplify_by_decoration = 1;
                revs->limited = 1;
                revs->prune = 1;
-               load_ref_decorations();
+               load_ref_decorations(DECORATE_SHORT_REFS);
        } else if (!strcmp(arg, "--date-order")) {
                revs->lifo = 0;
                revs->topo_order = 1;
@@ -1086,6 +1077,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->show_all = 1;
        } else if (!strcmp(arg, "--remove-empty")) {
                revs->remove_empty_trees = 1;
+       } else if (!strcmp(arg, "--merges")) {
+               revs->merges_only = 1;
        } else if (!strcmp(arg, "--no-merges")) {
                revs->no_merges = 1;
        } else if (!strcmp(arg, "--boundary")) {
@@ -1166,8 +1159,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->simplify_history = 0;
        } else if (!strcmp(arg, "--relative-date")) {
                revs->date_mode = DATE_RELATIVE;
+               revs->date_mode_explicit = 1;
        } else if (!strncmp(arg, "--date=", 7)) {
                revs->date_mode = parse_date_format(arg + 7);
+               revs->date_mode_explicit = 1;
        } else if (!strcmp(arg, "--log-size")) {
                revs->show_log_size = 1;
        }
@@ -1671,7 +1666,7 @@ static inline int want_ancestry(struct rev_info *revs)
        return (revs->rewrite_parents || revs->children.name);
 }
 
-enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
 {
        if (commit->object.flags & SHOWN)
                return commit_ignore;
@@ -1685,6 +1680,8 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
                return commit_ignore;
        if (revs->no_merges && commit->parents && commit->parents->next)
                return commit_ignore;
+       if (revs->merges_only && !(commit->parents && commit->parents->next))
+               return commit_ignore;
        if (!commit_match(commit, revs))
                return commit_ignore;
        if (revs->prune && revs->dense) {
@@ -1697,12 +1694,23 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
                        if (!commit->parents || !commit->parents->next)
                                return commit_ignore;
                }
-               if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0)
-                       return commit_error;
        }
        return commit_show;
 }
 
+enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+       enum commit_action action = get_commit_action(revs, commit);
+
+       if (action == commit_show &&
+           !revs->show_all &&
+           revs->prune && revs->dense && want_ancestry(revs)) {
+               if (rewrite_parents(revs, commit) < 0)
+                       return commit_error;
+       }
+       return action;
+}
+
 static struct commit *get_revision_1(struct rev_info *revs)
 {
        if (!revs->commits)
index be39e7d38643817cced4e07a89b6db5201f2b747..b6421a64321168237443ecb7e56e6b0765256c8b 100644 (file)
@@ -15,6 +15,9 @@
 #define SYMMETRIC_LEFT (1u<<8)
 #define ALL_REV_FLAGS  ((1u<<9)-1)
 
+#define DECORATE_SHORT_REFS    1
+#define DECORATE_FULL_REFS     2
+
 struct rev_info;
 struct log_info;
 
@@ -36,6 +39,7 @@ struct rev_info {
        unsigned int    dense:1,
                        prune:1,
                        no_merges:1,
+                       merges_only:1,
                        no_walk:1,
                        show_all:1,
                        remove_empty_trees:1,
@@ -77,7 +81,8 @@ struct rev_info {
                        show_merge:1,
                        abbrev_commit:1,
                        use_terminator:1,
-                       missing_newline:1;
+                       missing_newline:1,
+                       date_mode_explicit:1;
        enum date_mode date_mode;
 
        unsigned int    abbrev;
@@ -118,8 +123,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);
@@ -163,6 +169,7 @@ enum commit_action {
        commit_error
 };
 
+extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
 extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
 
 #endif
index eb2efc33073512efd14b65e67db8cf814ca3fa33..cf2d8f7fae1356e50736cb9d599625df79738a2a 100644 (file)
@@ -19,6 +19,7 @@ int start_command(struct child_process *cmd)
 {
        int need_in, need_out, need_err;
        int fdin[2], fdout[2], fderr[2];
+       int failed_errno = failed_errno;
 
        /*
         * In case of errors we must keep the promise to close FDs
@@ -28,9 +29,10 @@ int start_command(struct child_process *cmd)
        need_in = !cmd->no_stdin && cmd->in < 0;
        if (need_in) {
                if (pipe(fdin) < 0) {
+                       failed_errno = errno;
                        if (cmd->out > 0)
                                close(cmd->out);
-                       return -ERR_RUN_COMMAND_PIPE;
+                       goto fail_pipe;
                }
                cmd->in = fdin[1];
        }
@@ -40,11 +42,12 @@ int start_command(struct child_process *cmd)
                && cmd->out < 0;
        if (need_out) {
                if (pipe(fdout) < 0) {
+                       failed_errno = errno;
                        if (need_in)
                                close_pair(fdin);
                        else if (cmd->in)
                                close(cmd->in);
-                       return -ERR_RUN_COMMAND_PIPE;
+                       goto fail_pipe;
                }
                cmd->out = fdout[0];
        }
@@ -52,6 +55,7 @@ int start_command(struct child_process *cmd)
        need_err = !cmd->no_stderr && cmd->err < 0;
        if (need_err) {
                if (pipe(fderr) < 0) {
+                       failed_errno = errno;
                        if (need_in)
                                close_pair(fdin);
                        else if (cmd->in)
@@ -60,14 +64,18 @@ int start_command(struct child_process *cmd)
                                close_pair(fdout);
                        else if (cmd->out)
                                close(cmd->out);
-                       return -ERR_RUN_COMMAND_PIPE;
+fail_pipe:
+                       error("cannot create pipe for %s: %s",
+                               cmd->argv[0], strerror(failed_errno));
+                       errno = failed_errno;
+                       return -1;
                }
                cmd->err = fderr[0];
        }
 
        trace_argv_printf(cmd->argv, "trace: run_command:");
 
-#ifndef __MINGW32__
+#ifndef WIN32
        fflush(NULL);
        cmd->pid = fork();
        if (!cmd->pid) {
@@ -101,8 +109,8 @@ int start_command(struct child_process *cmd)
                }
 
                if (cmd->dir && chdir(cmd->dir))
-                       die("exec %s: cd to %s failed (%s)", cmd->argv[0],
-                           cmd->dir, strerror(errno));
+                       die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
+                           cmd->dir);
                if (cmd->env) {
                        for (; *cmd->env; cmd->env++) {
                                if (strchr(*cmd->env, '='))
@@ -122,7 +130,11 @@ int start_command(struct child_process *cmd)
                                strerror(errno));
                exit(127);
        }
+       if (cmd->pid < 0)
+               error("cannot fork() for %s: %s", cmd->argv[0],
+                       strerror(failed_errno = errno));
 #else
+{
        int s0 = -1, s1 = -1, s2 = -1;  /* backups of stdin, stdout, stderr */
        const char **sargv = cmd->argv;
        char **env = environ;
@@ -162,17 +174,17 @@ int start_command(struct child_process *cmd)
 
        if (cmd->dir)
                die("chdir in start_command() not implemented");
-       if (cmd->env) {
-               env = copy_environ();
-               for (; *cmd->env; cmd->env++)
-                       env = env_setenv(env, *cmd->env);
-       }
+       if (cmd->env)
+               env = make_augmented_environ(cmd->env);
 
        if (cmd->git_cmd) {
                cmd->argv = prepare_git_cmd(cmd->argv);
        }
 
        cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
+       failed_errno = errno;
+       if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
+               error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
 
        if (cmd->env)
                free_environ(env);
@@ -186,10 +198,10 @@ int start_command(struct child_process *cmd)
                dup2(s1, 1), close(s1);
        if (s2 >= 0)
                dup2(s2, 2), close(s2);
+}
 #endif
 
        if (cmd->pid < 0) {
-               int err = errno;
                if (need_in)
                        close_pair(fdin);
                else if (cmd->in)
@@ -200,9 +212,8 @@ int start_command(struct child_process *cmd)
                        close(cmd->out);
                if (need_err)
                        close_pair(fderr);
-               return err == ENOENT ?
-                       -ERR_RUN_COMMAND_EXEC :
-                       -ERR_RUN_COMMAND_FORK;
+               errno = failed_errno;
+               return -1;
        }
 
        if (need_in)
@@ -221,40 +232,51 @@ int start_command(struct child_process *cmd)
        return 0;
 }
 
-static int wait_or_whine(pid_t pid)
+static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
 {
-       for (;;) {
-               int status, code;
-               pid_t waiting = waitpid(pid, &status, 0);
-
-               if (waiting < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       error("waitpid failed (%s)", strerror(errno));
-                       return -ERR_RUN_COMMAND_WAITPID;
-               }
-               if (waiting != pid)
-                       return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
-               if (WIFSIGNALED(status))
-                       return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
-
-               if (!WIFEXITED(status))
-                       return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
+       int status, code = -1;
+       pid_t waiting;
+       int failed_errno = 0;
+
+       while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+               ;       /* nothing */
+
+       if (waiting < 0) {
+               failed_errno = errno;
+               error("waitpid for %s failed: %s", argv0, strerror(errno));
+       } else if (waiting != pid) {
+               error("waitpid is confused (%s)", argv0);
+       } else if (WIFSIGNALED(status)) {
+               code = WTERMSIG(status);
+               error("%s died of signal %d", argv0, code);
+               /*
+                * This return value is chosen so that code & 0xff
+                * mimics the exit code that a POSIX shell would report for
+                * a program that died from this signal.
+                */
+               code -= 128;
+       } else if (WIFEXITED(status)) {
                code = WEXITSTATUS(status);
-               switch (code) {
-               case 127:
-                       return -ERR_RUN_COMMAND_EXEC;
-               case 0:
-                       return 0;
-               default:
-                       return -code;
+               /*
+                * Convert special exit code when execvp failed.
+                */
+               if (code == 127) {
+                       code = -1;
+                       failed_errno = ENOENT;
+                       if (!silent_exec_failure)
+                               error("cannot run %s: %s", argv0,
+                                       strerror(ENOENT));
                }
+       } else {
+               error("waitpid is confused (%s)", argv0);
        }
+       errno = failed_errno;
+       return code;
 }
 
 int finish_command(struct child_process *cmd)
 {
-       return wait_or_whine(cmd->pid);
+       return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure);
 }
 
 int run_command(struct child_process *cmd)
@@ -274,6 +296,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd,
        cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
        cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
        cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+       cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
 }
 
 int run_command_v_opt(const char **argv, int opt)
@@ -292,8 +315,8 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
        return run_command(&cmd);
 }
 
-#ifdef __MINGW32__
-static __stdcall unsigned run_thread(void *data)
+#ifdef WIN32
+static unsigned __stdcall run_thread(void *data)
 {
        struct async *async = data;
        return async->proc(async->fd_for_proc, async->data);
@@ -308,7 +331,7 @@ int start_async(struct async *async)
                return error("cannot create pipe: %s", strerror(errno));
        async->out = pipe_out[0];
 
-#ifndef __MINGW32__
+#ifndef WIN32
        /* Flush stdio before fork() to avoid cloning buffers */
        fflush(NULL);
 
@@ -337,11 +360,8 @@ int start_async(struct async *async)
 
 int finish_async(struct async *async)
 {
-#ifndef __MINGW32__
-       int ret = 0;
-
-       if (wait_or_whine(async->pid))
-               ret = error("waitpid (async) failed");
+#ifndef WIN32
+       int ret = wait_or_whine(async->pid, "child process", 0);
 #else
        DWORD ret = 0;
        if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
@@ -385,15 +405,7 @@ int run_hook(const char *index_file, const char *name, ...)
                hook.env = env;
        }
 
-       ret = start_command(&hook);
+       ret = run_command(&hook);
        free(argv);
-       if (ret) {
-               warning("Could not spawn %s", argv[0]);
-               return ret;
-       }
-       ret = finish_command(&hook);
-       if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
-               warning("%s exited due to uncaught signal", argv[0]);
-
        return ret;
 }
index e3455028435eab958d5f86a3e86249f1704b9c1b..fb342090e3cac49f41689f9610bfe2d6c87c87c1 100644 (file)
@@ -1,17 +1,6 @@
 #ifndef RUN_COMMAND_H
 #define RUN_COMMAND_H
 
-enum {
-       ERR_RUN_COMMAND_FORK = 10000,
-       ERR_RUN_COMMAND_EXEC,
-       ERR_RUN_COMMAND_PIPE,
-       ERR_RUN_COMMAND_WAITPID,
-       ERR_RUN_COMMAND_WAITPID_WRONG_PID,
-       ERR_RUN_COMMAND_WAITPID_SIGNAL,
-       ERR_RUN_COMMAND_WAITPID_NOEXIT,
-};
-#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK)
-
 struct child_process {
        const char **argv;
        pid_t pid;
@@ -42,6 +31,7 @@ struct child_process {
        unsigned no_stdout:1;
        unsigned no_stderr:1;
        unsigned git_cmd:1; /* if this is to be git sub-command */
+       unsigned silent_exec_failure:1;
        unsigned stdout_to_stderr:1;
        void (*preexec_cb)(void);
 };
@@ -55,6 +45,7 @@ extern int run_hook(const char *index_file, const char *name, ...);
 #define RUN_COMMAND_NO_STDIN 1
 #define RUN_GIT_CMD         2  /*If this is to be git sub-command */
 #define RUN_COMMAND_STDOUT_TO_STDERR 4
+#define RUN_SILENT_EXEC_FAILURE 8
 int run_command_v_opt(const char **argv, int opt);
 
 /*
@@ -79,7 +70,7 @@ struct async {
        int (*proc)(int fd, void *data);
        void *data;
        int out;        /* caller reads from here and closes it */
-#ifndef __MINGW32__
+#ifndef WIN32
        pid_t pid;
 #else
        HANDLE tid;
index 83d76c7e35183ded9eec5a56c59d4da549c0b584..8b3cf028ed1c54f8a513ad0bcddc847ad47fe602 100644 (file)
@@ -3,9 +3,11 @@
 
 struct send_pack_args {
        unsigned verbose:1,
+               quiet:1,
                send_mirror:1,
                force_update:1,
                use_thin_pack:1,
+               use_ofs_delta:1,
                dry_run:1;
 };
 
index 906ce5b27242ca1e415a5410135d58f1c000e8b0..4098ca2b5c166c32cfe4aea9d1bff2e6593e9a60 100644 (file)
@@ -246,7 +246,7 @@ int update_server_info(int force)
        errs = errs | update_info_packs(force);
 
        /* remove leftover rev-cache file if there is any */
-       unlink(git_path("info/rev-cache"));
+       unlink_or_warn(git_path("info/rev-cache"));
 
        return errs;
 }
diff --git a/setup.c b/setup.c
index ebd60de9ce5b52f348819a6a390c15b8dc08d2ff..029371e5844a1069d62456c6bb51028efbb671c4 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -41,7 +41,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
        static char path[PATH_MAX];
-#ifndef __MINGW32__
+#ifndef WIN32
        if (!pfx || !*pfx || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
@@ -81,7 +81,7 @@ void verify_filename(const char *prefix, const char *arg)
        if (errno == ENOENT)
                die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
                    "Use '--' to separate paths from revisions", arg);
-       die("'%s': %s", arg, strerror(errno));
+       die_errno("failed to stat '%s'", arg);
 }
 
 /*
@@ -103,7 +103,7 @@ void verify_non_filename(const char *prefix, const char *arg)
                die("ambiguous argument '%s': both revision and filename\n"
                    "Use '--' to separate filenames from revisions", arg);
        if (errno != ENOENT && errno != ENOTDIR)
-               die("'%s': %s", arg, strerror(errno));
+               die_errno("failed to stat '%s'", arg);
 }
 
 const char **get_pathspec(const char *prefix, const char **pathspec)
@@ -257,7 +257,7 @@ const char *read_gitfile_gently(const char *path)
                return NULL;
        fd = open(path, O_RDONLY);
        if (fd < 0)
-               die("Error opening %s: %s", path, strerror(errno));
+               die_errno("Error opening '%s'", path);
        buf = xmalloc(st.st_size + 1);
        len = read_in_full(fd, buf, st.st_size);
        close(fd);
@@ -327,7 +327,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
                                return NULL;
                        set_git_dir(make_absolute_path(gitdirenv));
                        if (chdir(work_tree_env) < 0)
-                               die ("Could not chdir to %s", work_tree_env);
+                               die_errno ("Could not chdir to '%s'", work_tree_env);
                        strcat(buffer, "/");
                        return retval;
                }
@@ -339,7 +339,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
        }
 
        if (!getcwd(cwd, sizeof(cwd)-1))
-               die("Unable to read current working directory");
+               die_errno("Unable to read current working directory");
 
        ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
        if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
@@ -382,14 +382,14 @@ const char *setup_git_directory_gently(int *nongit_ok)
                if (offset <= ceil_offset) {
                        if (nongit_ok) {
                                if (chdir(cwd))
-                                       die("Cannot come back to cwd");
+                                       die_errno("Cannot come back to cwd");
                                *nongit_ok = 1;
                                return NULL;
                        }
                        die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
                }
                if (chdir(".."))
-                       die("Cannot change to %s/..: %s", cwd, strerror(errno));
+                       die_errno("Cannot change to '%s/..'", cwd);
        }
 
        inside_git_dir = 0;
@@ -493,10 +493,10 @@ const char *setup_git_directory(void)
                static char buffer[PATH_MAX + 1];
                char *rel;
                if (retval && chdir(retval))
-                       die ("Could not jump back into original cwd");
+                       die_errno ("Could not jump back into original cwd");
                rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
                if (rel && *rel && chdir(get_git_work_tree()))
-                       die ("Could not jump to working directory");
+                       die_errno ("Could not jump to working directory");
                return rel && *rel ? strcat(rel, "/") : NULL;
        }
 
index 28bd9082fc9f4b79519f6bef8204600072c07027..4cc8939e4b901c1eda75a71705021c749c9a48b0 100644 (file)
@@ -720,6 +720,8 @@ static int open_packed_git_1(struct packed_git *p)
                return error("packfile %s index unavailable", p->pack_name);
 
        p->pack_fd = open(p->pack_name, O_RDONLY);
+       while (p->pack_fd < 0 && errno == EMFILE && unuse_one_window(p, -1))
+               p->pack_fd = open(p->pack_name, O_RDONLY);
        if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
                return -1;
 
@@ -937,6 +939,8 @@ static void prepare_packed_git_one(char *objdir, int local)
        sprintf(path, "%s/pack", objdir);
        len = strlen(path);
        dir = opendir(path);
+       while (!dir && errno == EMFILE && unuse_one_window(packed_git, -1))
+               dir = opendir(path);
        if (!dir) {
                if (errno != ENOENT)
                        error("unable to open object pack directory: %s: %s",
@@ -1158,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++];
@@ -1167,7 +1170,7 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
        size = c & 15;
        shift = 4;
        while (c & 0x80) {
-               if (len <= used || sizeof(long) * 8 <= shift) {
+               if (len <= used || bitsizeof(long) <= shift) {
                        error("bad object header");
                        return 0;
                }
@@ -1354,6 +1357,8 @@ unsigned long get_size_from_delta(struct packed_git *p,
                in = use_pack(p, w_curs, curpos, &stream.avail_in);
                stream.next_in = in;
                st = git_inflate(&stream, Z_FINISH);
+               if (st == Z_BUF_ERROR && (stream.avail_in || !stream.avail_out))
+                       break;
                curpos += stream.next_in - in;
        } while ((st == Z_OK || st == Z_BUF_ERROR) &&
                 stream.total_out < sizeof(delta_head));
@@ -1591,6 +1596,8 @@ static void *unpack_compressed_entry(struct packed_git *p,
                in = use_pack(p, w_curs, curpos, &stream.avail_in);
                stream.next_in = in;
                st = git_inflate(&stream, Z_FINISH);
+               if (st == Z_BUF_ERROR && (stream.avail_in || !stream.avail_out))
+                       break;
                curpos += stream.next_in - in;
        } while (st == Z_OK || st == Z_BUF_ERROR);
        git_inflate_end(&stream);
@@ -2141,13 +2148,26 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
        return read_packed_sha1(sha1, type, size);
 }
 
-void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
-                    unsigned long *size)
+void *read_sha1_file_repl(const unsigned char *sha1,
+                         enum object_type *type,
+                         unsigned long *size,
+                         const unsigned char **replacement)
 {
-       void *data = read_object(sha1, type, size);
+       const unsigned char *repl = lookup_replace_object(sha1);
+       void *data = read_object(repl, type, size);
+
+       /* die if we replaced an object with one that does not exist */
+       if (!data && repl != sha1)
+               die("replacement %s not found for %s",
+                   sha1_to_hex(repl), sha1_to_hex(sha1));
+
        /* legacy behavior is to die on corrupted objects */
-       if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1)))
-               die("object %s is corrupted", sha1_to_hex(sha1));
+       if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
+               die("object %s is corrupted", sha1_to_hex(repl));
+
+       if (replacement)
+               *replacement = repl;
+
        return data;
 }
 
@@ -2247,7 +2267,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
                        goto out;
                ret = errno;
        }
-       unlink(tmpfile);
+       unlink_or_warn(tmpfile);
        if (ret) {
                if (ret != EEXIST) {
                        return error("unable to write sha1 filename %s: %s\n", filename, strerror(ret));
@@ -2283,7 +2303,7 @@ static void close_sha1_file(int fd)
        if (fsync_object_files)
                fsync_or_die(fd, "sha1 file");
        if (close(fd) != 0)
-               die("error when closing sha1 file (%s)", strerror(errno));
+               die_errno("error when closing sha1 file");
 }
 
 /* Size of directory component, including the ending '/' */
@@ -2339,6 +2359,8 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 
        filename = sha1_file_name(sha1);
        fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
+       while (fd < 0 && errno == EMFILE && unuse_one_window(packed_git, -1))
+               fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
        if (fd < 0) {
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
index 904bcd96a54a1cc33386a56a16d07dce34cbb90b..44bb62d270739a232e87c90c05ce89fcc86bc15b 100644 (file)
@@ -777,8 +777,6 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
        for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
        if (cb.cnt < nth) {
                cb.cnt = 0;
-               for (i = 0; i < nth; i++)
-                       strbuf_release(&cb.buf[i]);
                for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
        }
        if (cb.cnt < nth)
diff --git a/shell.c b/shell.c
index b968be79f487f8d93e205fd0a844a206e24f21f8..e4864e04da3b0e237342c2ca0548c0ec0082c171 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -60,7 +60,7 @@ int main(int argc, char **argv)
        while (devnull_fd >= 0 && devnull_fd <= 2)
                devnull_fd = dup(devnull_fd);
        if (devnull_fd == -1)
-               die("opening /dev/null failed (%s)", strerror(errno));
+               die_errno("opening /dev/null failed");
        close (devnull_fd);
 
        /*
index a88496030b7053a543173c299bd9f54b923db2ec..a6153dca278abe957254fa091fdcd8eb13b90b25 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -260,7 +260,7 @@ size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
        res = fread(sb->buf + sb->len, 1, size, f);
        if (res > 0)
                strbuf_setlen(sb, sb->len + res);
-       else if (res < 0 && oldalloc == 0)
+       else if (oldalloc == 0)
                strbuf_release(sb);
        return res;
 }
@@ -322,7 +322,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
        return -1;
 }
 
-int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
 {
        int ch;
 
@@ -332,10 +332,10 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
 
        strbuf_reset(sb);
        while ((ch = fgetc(fp)) != EOF) {
-               if (ch == term)
-                       break;
                strbuf_grow(sb, 1);
                sb->buf[sb->len++] = ch;
+               if (ch == term)
+                       break;
        }
        if (ch == EOF && sb->len == 0)
                return EOF;
@@ -344,6 +344,15 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
        return 0;
 }
 
+int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+{
+       if (strbuf_getwholeline(sb, fp, term))
+               return EOF;
+       if (sb->buf[sb->len-1] == term)
+               strbuf_setlen(sb, sb->len-1);
+       return 0;
+}
+
 int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
 {
        int fd, len;
index eaa8704d5fa6e85d33953db77dd03de3cb462df4..d05e056dd3916b64865ac9a0337ecbf31625f436 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -126,6 +126,7 @@ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
 extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 
+extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
 extern int strbuf_getline(struct strbuf *, FILE *, int);
 
 extern void stripspace(struct strbuf *buf, int skip_comments);
index 1d6b35b858020300f502e2a9341b82d4fa8a61fd..7b0a86d35776e8695423c13403c9f4fa3465017d 100644 (file)
@@ -32,19 +32,13 @@ static int longest_path_match(const char *name_a, int len_a,
        return match_len;
 }
 
-static struct cache_def {
-       char path[PATH_MAX + 1];
-       int len;
-       int flags;
-       int track_flags;
-       int prefix_len_stat_func;
-} cache;
+static struct cache_def default_cache;
 
-static inline void reset_lstat_cache(void)
+static inline void reset_lstat_cache(struct cache_def *cache)
 {
-       cache.path[0] = '\0';
-       cache.len = 0;
-       cache.flags = 0;
+       cache->path[0] = '\0';
+       cache->len = 0;
+       cache->flags = 0;
        /*
         * The track_flags and prefix_len_stat_func members is only
         * set by the safeguard rule inside lstat_cache()
@@ -70,23 +64,23 @@ static inline void reset_lstat_cache(void)
  * of the prefix, where the cache should use the stat() function
  * instead of the lstat() function to test each path component.
  */
-static int lstat_cache(const char *name, int len,
+static int lstat_cache(struct cache_def *cache, const char *name, int len,
                       int track_flags, int prefix_len_stat_func)
 {
        int match_len, last_slash, last_slash_dir, previous_slash;
        int match_flags, ret_flags, save_flags, max_len, ret;
        struct stat st;
 
-       if (cache.track_flags != track_flags ||
-           cache.prefix_len_stat_func != prefix_len_stat_func) {
+       if (cache->track_flags != track_flags ||
+           cache->prefix_len_stat_func != prefix_len_stat_func) {
                /*
                 * As a safeguard rule we clear the cache if the
                 * values of track_flags and/or prefix_len_stat_func
                 * does not match with the last supplied values.
                 */
-               reset_lstat_cache();
-               cache.track_flags = track_flags;
-               cache.prefix_len_stat_func = prefix_len_stat_func;
+               reset_lstat_cache(cache);
+               cache->track_flags = track_flags;
+               cache->prefix_len_stat_func = prefix_len_stat_func;
                match_len = last_slash = 0;
        } else {
                /*
@@ -94,10 +88,14 @@ static int lstat_cache(const char *name, int len,
                 * the 2 "excluding" path types.
                 */
                match_len = last_slash =
-                       longest_path_match(name, len, cache.path, cache.len,
+                       longest_path_match(name, len, cache->path, cache->len,
                                           &previous_slash);
-               match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
-               if (match_flags && match_len == cache.len)
+               match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
+
+               if (!(track_flags & FL_FULLPATH) && match_len == len)
+                       match_len = last_slash = previous_slash;
+
+               if (match_flags && match_len == cache->len)
                        return match_flags;
                /*
                 * If we now have match_len > 0, we would know that
@@ -121,18 +119,18 @@ static int lstat_cache(const char *name, int len,
        max_len = len < PATH_MAX ? len : PATH_MAX;
        while (match_len < max_len) {
                do {
-                       cache.path[match_len] = name[match_len];
+                       cache->path[match_len] = name[match_len];
                        match_len++;
                } while (match_len < max_len && name[match_len] != '/');
                if (match_len >= max_len && !(track_flags & FL_FULLPATH))
                        break;
                last_slash = match_len;
-               cache.path[last_slash] = '\0';
+               cache->path[last_slash] = '\0';
 
                if (last_slash <= prefix_len_stat_func)
-                       ret = stat(cache.path, &st);
+                       ret = stat(cache->path, &st);
                else
-                       ret = lstat(cache.path, &st);
+                       ret = lstat(cache->path, &st);
 
                if (ret) {
                        ret_flags = FL_LSTATERR;
@@ -156,9 +154,9 @@ static int lstat_cache(const char *name, int len,
         */
        save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
        if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
-               cache.path[last_slash] = '\0';
-               cache.len = last_slash;
-               cache.flags = save_flags;
+               cache->path[last_slash] = '\0';
+               cache->len = last_slash;
+               cache->flags = save_flags;
        } else if ((track_flags & FL_DIR) &&
                   last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
                /*
@@ -172,11 +170,11 @@ static int lstat_cache(const char *name, int len,
                 * can still cache the path components before the last
                 * one (the found symlink or non-existing component).
                 */
-               cache.path[last_slash_dir] = '\0';
-               cache.len = last_slash_dir;
-               cache.flags = FL_DIR;
+               cache->path[last_slash_dir] = '\0';
+               cache->len = last_slash_dir;
+               cache->flags = FL_DIR;
        } else {
-               reset_lstat_cache();
+               reset_lstat_cache(cache);
        }
        return ret_flags;
 }
@@ -188,16 +186,18 @@ static int lstat_cache(const char *name, int len,
 void invalidate_lstat_cache(const char *name, int len)
 {
        int match_len, previous_slash;
+       struct cache_def *cache = &default_cache;       /* FIXME */
 
-       match_len = longest_path_match(name, len, cache.path, cache.len,
+       match_len = longest_path_match(name, len, cache->path, cache->len,
                                       &previous_slash);
        if (len == match_len) {
-               if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
-                       cache.path[previous_slash] = '\0';
-                       cache.len = previous_slash;
-                       cache.flags = FL_DIR;
-               } else
-                       reset_lstat_cache();
+               if ((cache->track_flags & FL_DIR) && previous_slash > 0) {
+                       cache->path[previous_slash] = '\0';
+                       cache->len = previous_slash;
+                       cache->flags = FL_DIR;
+               } else {
+                       reset_lstat_cache(cache);
+               }
        }
 }
 
@@ -206,19 +206,26 @@ void invalidate_lstat_cache(const char *name, int len)
  */
 void clear_lstat_cache(void)
 {
-       reset_lstat_cache();
+       struct cache_def *cache = &default_cache;       /* FIXME */
+       reset_lstat_cache(cache);
 }
 
 #define USE_ONLY_LSTAT  0
 
+/*
+ * Return non-zero if path 'name' has a leading symlink component
+ */
+int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name, int len)
+{
+       return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK;
+}
+
 /*
  * Return non-zero if path 'name' has a leading symlink component
  */
 int has_symlink_leading_path(const char *name, int len)
 {
-       return lstat_cache(name, len,
-                          FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
-               FL_SYMLINK;
+       return threaded_has_symlink_leading_path(&default_cache, name, len);
 }
 
 /*
@@ -227,7 +234,8 @@ int has_symlink_leading_path(const char *name, int len)
  */
 int has_symlink_or_noent_leading_path(const char *name, int len)
 {
-       return lstat_cache(name, len,
+       struct cache_def *cache = &default_cache;       /* FIXME */
+       return lstat_cache(cache, name, len,
                           FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
                (FL_SYMLINK|FL_NOENT);
 }
@@ -241,7 +249,8 @@ int has_symlink_or_noent_leading_path(const char *name, int len)
  */
 int has_dirs_only_path(const char *name, int len, int prefix_len)
 {
-       return lstat_cache(name, len,
+       struct cache_def *cache = &default_cache;       /* FIXME */
+       return lstat_cache(cache, name, len,
                           FL_DIR|FL_FULLPATH, prefix_len) &
                FL_DIR;
 }
@@ -263,7 +272,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 +304,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 bf816fc8505508c91999175ad6544a67febabb33..bd09390d3208d7eac362cd9cf45f7dde623c4ae6 100644 (file)
@@ -3,6 +3,8 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
+-include ../config.mak
+
 #GIT_TEST_OPTS=--verbose --debug
 SHELL_PATH ?= $(SHELL)
 TAR ?= $(TAR)
diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh
new file mode 100644 (file)
index 0000000..8452532
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+gitweb_init () {
+       safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
+       cat >gitweb_config.perl <<EOF
+#!/usr/bin/perl
+
+# gitweb configuration for tests
+
+our \$version = 'current';
+our \$GIT = 'git';
+our \$projectroot = "$safe_pwd";
+our \$project_maxdepth = 8;
+our \$home_link_str = 'projects';
+our \$site_name = '[localhost]';
+our \$site_header = '';
+our \$site_footer = '';
+our \$home_text = 'indextext.html';
+our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/gitweb.css');
+our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/git-logo.png';
+our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/git-favicon.png';
+our \$projects_list = '';
+our \$export_ok = '';
+our \$strict_export = '';
+
+EOF
+
+       cat >.git/description <<EOF
+$0 test repository
+EOF
+}
+
+gitweb_run () {
+       GATEWAY_INTERFACE='CGI/1.1'
+       HTTP_ACCEPT='*/*'
+       REQUEST_METHOD='GET'
+       SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
+       QUERY_STRING=""$1""
+       PATH_INFO=""$2""
+       export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
+               SCRIPT_NAME QUERY_STRING PATH_INFO
+
+       GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+       export GITWEB_CONFIG
+
+       # some of git commands write to STDERR on error, but this is not
+       # written to web server logs, so we are not interested in that:
+       # we are interested only in properly formatted errors/warnings
+       rm -f gitweb.log &&
+       perl -- "$SCRIPT_NAME" \
+               >gitweb.output 2>gitweb.log &&
+       if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi
+
+       # gitweb.log is left for debugging
+       # gitweb.output is used to parse http output
+}
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+       say 'skipping gitweb tests, perl not available'
+       test_done
+fi
+
+perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
+    say 'skipping gitweb tests, perl version is too old'
+    test_done
+}
+
+gitweb_init
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
new file mode 100644 (file)
index 0000000..4b3b793
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+. ./test-lib.sh
+
+unset CVS_SERVER
+# for clean cvsps cache
+HOME=$(pwd)
+export HOME
+
+if ! type cvs >/dev/null 2>&1
+then
+       say 'skipping cvsimport tests, cvs not found'
+       test_done
+fi
+
+CVS="cvs -f"
+export CVS
+
+cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
+case "$cvsps_version" in
+2.1 | 2.2*)
+       ;;
+'')
+       say 'skipping cvsimport tests, cvsps not found'
+       test_done
+       ;;
+*)
+       say 'skipping cvsimport tests, unsupported cvsps version'
+       test_done
+       ;;
+esac
+
+test_cvs_co () {
+       # Usage: test_cvs_co BRANCH_NAME
+       rm -rf module-cvs-"$1"
+       if [ "$1" = "master" ]
+       then
+               $CVS co -P -d module-cvs-"$1" -A module
+       else
+               $CVS co -P -d module-cvs-"$1" -r "$1" module
+       fi
+}
+
+test_git_co () {
+       # Usage: test_git_co BRANCH_NAME
+       (cd module-git && git checkout "$1")
+}
+
+test_cmp_branch_file () {
+       # Usage: test_cmp_branch_file BRANCH_NAME PATH
+       # The branch must already be checked out of CVS and git.
+       test_cmp module-cvs-"$1"/"$2" module-git/"$2"
+}
+
+test_cmp_branch_tree () {
+       # Usage: test_cmp_branch_tree BRANCH_NAME
+       # Check BRANCH_NAME out of CVS and git and make sure that all
+       # of the files and directories are identical.
+
+       test_cvs_co "$1" &&
+       test_git_co "$1" &&
+       (
+               cd module-cvs-"$1"
+               find . -type d -name CVS -prune -o -type f -print
+       ) | sort >module-cvs-"$1".list &&
+       (
+               cd module-git
+               find . -type d -name .git -prune -o -type f -print
+       ) | sort >module-git-"$1".list &&
+       test_cmp module-cvs-"$1".list module-git-"$1".list &&
+       cat module-cvs-"$1".list | while read f
+       do
+               test_cmp_branch_file "$1" "$f" || return 1
+       done
+}
index 773d47cf3cb704d7976185738a2b9840c159a33c..fd8631f90686bb4dfe6d39290434136fc4741068 100644 (file)
@@ -14,7 +14,7 @@ if ! test_have_prereq PERL; then
 fi
 
 GIT_DIR=$PWD/.git
-GIT_SVN_DIR=$GIT_DIR/svn/git-svn
+GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
 SVN_TREE=$GIT_SVN_DIR/svn-tree
 
 svn >/dev/null 2>&1
@@ -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
 }
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
new file mode 100755 (executable)
index 0000000..75a3ee2
--- /dev/null
@@ -0,0 +1,41 @@
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+       say 'skipping --patch tests, perl not available'
+       test_done
+fi
+
+set_state () {
+       echo "$3" > "$1" &&
+       git add "$1" &&
+       echo "$2" > "$1"
+}
+
+save_state () {
+       noslash="$(echo "$1" | tr / _)" &&
+       cat "$1" > _worktree_"$noslash" &&
+       git show :"$1" > _index_"$noslash"
+}
+
+set_and_save_state () {
+       set_state "$@" &&
+       save_state "$1"
+}
+
+verify_state () {
+       test "$(cat "$1")" = "$2" &&
+       test "$(git show :"$1")" = "$3"
+}
+
+verify_saved_state () {
+       noslash="$(echo "$1" | tr / _)" &&
+       verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
+}
+
+save_head () {
+       git rev-parse HEAD > _head
+}
+
+verify_saved_head () {
+       test "$(cat _head)" = "$(git rev-parse HEAD)"
+}
index e3d846420dc09e7b24876b291b9e546ac0628ed3..5386504790deea55d127f053f7b714cd121a2d57 100755 (executable)
@@ -208,4 +208,87 @@ test_expect_success 'init rejects insanely long --template' '
        )
 '
 
+test_expect_success 'init creates a new directory' '
+       rm -fr newdir &&
+       (
+               git init newdir &&
+               test -d newdir/.git/refs
+       )
+'
+
+test_expect_success 'init creates a new bare directory' '
+       rm -fr newdir &&
+       (
+               git init --bare newdir &&
+               test -d newdir/refs
+       )
+'
+
+test_expect_success 'init recreates a directory' '
+       rm -fr newdir &&
+       (
+               mkdir newdir &&
+               git init newdir &&
+               test -d newdir/.git/refs
+       )
+'
+
+test_expect_success 'init recreates a new bare directory' '
+       rm -fr newdir &&
+       (
+               mkdir newdir &&
+               git init --bare newdir &&
+               test -d newdir/refs
+       )
+'
+
+test_expect_success 'init creates a new deep directory' '
+       rm -fr newdir &&
+       git init newdir/a/b/c &&
+       test -d newdir/a/b/c/.git/refs
+'
+
+test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shared)' '
+       rm -fr newdir &&
+       (
+               # Leading directories should honor umask while
+               # the repository itself should follow "shared"
+               umask 002 &&
+               git init --bare --shared=0660 newdir/a/b/c &&
+               test -d newdir/a/b/c/refs &&
+               ls -ld newdir/a newdir/a/b > lsab.out &&
+               ! grep -v "^drwxrw[sx]r-x" lsab.out &&
+               ls -ld newdir/a/b/c > lsc.out &&
+               ! grep -v "^drwxrw[sx]---" lsc.out
+       )
+'
+
+test_expect_success 'init notices EEXIST (1)' '
+       rm -fr newdir &&
+       (
+               >newdir &&
+               test_must_fail git init newdir &&
+               test -f newdir
+       )
+'
+
+test_expect_success 'init notices EEXIST (2)' '
+       rm -fr newdir &&
+       (
+               mkdir newdir &&
+               >newdir/a
+               test_must_fail git init newdir/a/b &&
+               test -f newdir/a
+       )
+'
+
+test_expect_success POSIXPERM 'init notices EPERM' '
+       rm -fr newdir &&
+       (
+               mkdir newdir &&
+               chmod -w newdir &&
+               test_must_fail git init newdir/a/b
+       )
+'
+
 test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
new file mode 100755 (executable)
index 0000000..75b02af
--- /dev/null
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+test_description='test date parsing and printing'
+. ./test-lib.sh
+
+# arbitrary reference time: 2009-08-30 19:20:00
+TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
+
+check_show() {
+       t=$(($TEST_DATE_NOW - $1))
+       echo "$t -> $2" >expect
+       test_expect_${3:-success} "relative date ($2)" "
+       test-date show $t >actual &&
+       test_cmp expect actual
+       "
+}
+
+check_show 5 '5 seconds ago'
+check_show 300 '5 minutes ago'
+check_show 18000 '5 hours ago'
+check_show 432000 '5 days ago'
+check_show 1728000 '3 weeks ago'
+check_show 13000000 '5 months ago'
+check_show 37500000 '1 year, 2 months ago'
+check_show 55188000 '1 year, 9 months ago'
+check_show 630000000 '20 years ago'
+check_show 31449600 '12 months ago'
+
+check_parse() {
+       echo "$1 -> $2" >expect
+       test_expect_${3:-success} "parse date ($1)" "
+       test-date parse '$1' >actual &&
+       test_cmp expect actual
+       "
+}
+
+check_parse 2008 bad
+check_parse 2008-02 bad
+check_parse 2008-02-14 bad
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
+
+check_approxidate() {
+       echo "$1 -> $2 +0000" >expect
+       test_expect_${3:-success} "parse approxidate ($1)" "
+       test-date approxidate '$1' >actual &&
+       test_cmp expect actual
+       "
+}
+
+check_approxidate now '2009-08-30 19:20:00'
+check_approxidate '5 seconds ago' '2009-08-30 19:19:55'
+check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
+check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
+check_approxidate yesterday '2009-08-29 19:20:00'
+check_approxidate 3.days.ago '2009-08-27 19:20:00'
+check_approxidate 3.weeks.ago '2009-08-09 19:20:00'
+check_approxidate 3.months.ago '2009-05-30 19:20:00'
+check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00'
+
+check_approxidate '6am yesterday' '2009-08-29 06:00:00'
+check_approxidate '6pm yesterday' '2009-08-29 18:00:00'
+check_approxidate '3:00' '2009-08-30 03:00:00'
+check_approxidate '15:00' '2009-08-30 15:00:00'
+check_approxidate 'noon today' '2009-08-30 12:00:00'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+
+check_approxidate 'last tuesday' '2009-08-25 19:20:00'
+check_approxidate 'July 5th' '2009-07-05 19:20:00'
+check_approxidate '06/05/2009' '2009-06-05 19:20:00'
+check_approxidate '06.05.2009' '2009-05-06 19:20:00'
+
+check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
+check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
+check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
+
+test_done
index e38241c80a6625c9b5b89340b9d0c5a2667bf345..3d450ed379fcd1bf480fc75feb0e6ddb8f05e5be 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,10 @@ String options
 
 Magic arguments
     --quux                means --quux
+    -NUM                  set integer to NUM
+    +                     same as -b
+    --ambiguous           positive ambiguity
+    --no-ambiguous        negative ambiguity
 
 Standard options
     --abbrev[=<n>]        use <n> digits to display SHA-1s
@@ -53,10 +59,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 +78,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 +93,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 +107,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 arg 00: a1
 arg 01: b1
 arg 02: --boolean
@@ -117,6 +129,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 EOF
 
 test_expect_success 'unambiguously abbreviated option' '
@@ -145,6 +158,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 +186,7 @@ abbrev: 7
 verbose: 0
 quiet: no
 dry run: no
+file: (not set)
 arg 00: --quux
 EOF
 
@@ -190,6 +205,7 @@ abbrev: 7
 verbose: 0
 quiet: yes
 dry run: no
+file: (not set)
 arg 00: foo
 EOF
 
@@ -210,6 +226,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 +254,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 +263,76 @@ 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
+'
+
+cat >expect <<EOF
+boolean: 0
+integer: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
+       test-parse-options --no-ambig >output 2>output.err &&
+       test ! -s output.err &&
+       test_cmp expect output
+'
 
 test_done
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
new file mode 100755 (executable)
index 0000000..59b3aa4
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='test read-tree into a fresh index file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo one >a &&
+       git add a &&
+       git commit -m initial
+'
+
+test_expect_success 'non-existent index file' '
+       rm -f new-index &&
+       GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_expect_success 'empty index file' '
+       rm -f new-index &&
+       > new-index &&
+       GIT_INDEX_FILE=new-index git read-tree master
+'
+
+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 43ea283242c4afc25e53d4a8c894140793649717..83b7294010cd59d5438b6020868c699c01105595 100755 (executable)
@@ -459,6 +459,28 @@ EOF
 
 test_expect_success "rename succeeded" "test_cmp expect .git/config"
 
+cat >> .git/config << EOF
+[branch "vier"] z = 1
+EOF
+
+test_expect_success "rename a section with a var on the same line" \
+       'git config --rename-section branch.vier branch.zwei'
+
+cat > expect << EOF
+# Hallo
+       #Bello
+[branch "zwei"]
+       x = 1
+[branch "zwei"]
+       y = 1
+[branch "drei"]
+weird
+[branch "zwei"]
+       z = 1
+EOF
+
+test_expect_success "rename succeeded" "test_cmp expect .git/config"
+
 cat >> .git/config << EOF
   [branch "zwei"] a = 1 [branch "vier"]
 EOF
@@ -733,6 +755,11 @@ echo >>result
 
 test_expect_success '--null --get-regexp' 'cmp result expect'
 
+test_expect_success 'inner whitespace kept verbatim' '
+       git config section.val "foo       bar" &&
+       test "z$(git config section.val)" = "zfoo         bar"
+'
+
 test_expect_success SYMLINKS 'symlinked configuration' '
 
        ln -s notyet myconfig &&
index 997002d4c40dd8e66e3be5a701e3d99bab1c57c4..e5040580626f4df6159c929c1ad54f085f03d830 100755 (executable)
@@ -20,8 +20,7 @@ Extras
 
 EOF
 
-test_expect_success 'test --parseopt help output' '
-       git rev-parse --parseopt -- -h 2> output.err <<EOF
+cat > optionspec << EOF
 some-command [options] <args>...
 
 some-command does foo and bar!
@@ -37,7 +36,47 @@ C?        option C with an optional argument
 Extras
 extra1    line above used to cause a segfault but no longer does
 EOF
+
+test_expect_success 'test --parseopt help output' '
+       git rev-parse --parseopt -- -h 2> output.err < optionspec
        test_cmp expect.err output.err
 '
 
+cat > expect <<EOF
+set -- --foo --bar 'ham' -- 'arg'
+EOF
+
+test_expect_success 'test --parseopt' '
+       git rev-parse --parseopt -- --foo --bar=ham arg < optionspec > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'test --parseopt with mixed options and arguments' '
+       git rev-parse --parseopt -- --foo arg --bar=ham < optionspec > output &&
+       test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- 'arg' '--bar=ham'
+EOF
+
+test_expect_success 'test --parseopt with --' '
+       git rev-parse --parseopt -- --foo -- arg --bar=ham < optionspec > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'test --parseopt --stop-at-non-option' '
+       git rev-parse --parseopt --stop-at-non-option -- --foo arg --bar=ham < optionspec > output &&
+       test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- '--' 'arg' '--bar=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash' '
+       git rev-parse --parseopt --keep-dashdash -- --foo -- arg --bar=ham < optionspec > output &&
+       test_cmp expect output
+'
+
 test_done
index f7e1a735ec8699616280a086f59dc50c078bfaa7..de3edb5d571ea83263f5133e751704ab0ba580c8 100755 (executable)
@@ -48,4 +48,13 @@ test_expect_success \
     'git checkout-index conflicting paths.' \
     'test -f path0 && test -d path1 && test -f path1/file1'
 
+test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
+       mkdir -p tar/get &&
+       ln -s tar/get there &&
+       echo first &&
+       git checkout-index -a -f --prefix=there/ &&
+       echo second &&
+       git checkout-index -a -f --prefix=there/
+'
+
 test_done
diff --git a/t/t2014-switch.sh b/t/t2014-switch.sh
new file mode 100755 (executable)
index 0000000..ccfb147
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='Peter MacMillan'
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo Hello >file &&
+       git add file &&
+       test_tick &&
+       git commit -m V1 &&
+       echo Hello world >file &&
+       git add file &&
+       git checkout -b other
+'
+
+test_expect_success 'check all changes are staged' '
+       git diff --exit-code
+'
+
+test_expect_success 'second commit' '
+       git commit -m V2
+'
+
+test_expect_success 'check' '
+       git diff --cached --exit-code
+'
+
+test_done
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
new file mode 100755 (executable)
index 0000000..c551d39
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='checkout from unborn branch protects contents'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir parent &&
+       (cd parent &&
+        git init &&
+        echo content >file &&
+        git add file &&
+        git commit -m base
+       ) &&
+       git fetch parent master:origin
+'
+
+test_expect_success 'checkout from unborn preserves untracked files' '
+       echo precious >expect &&
+       echo precious >file &&
+       test_must_fail git checkout -b new origin &&
+       test_cmp expect file
+'
+
+test_expect_success 'checkout from unborn preserves index contents' '
+       echo precious >expect &&
+       echo precious >file &&
+       git add file &&
+       test_must_fail git checkout -b new origin &&
+       test_cmp expect file &&
+       git show :file >file &&
+       test_cmp expect file
+'
+
+test_expect_success 'checkout from unborn merges identical index contents' '
+       echo content >file &&
+       git add file &&
+       git checkout -b new origin
+'
+
+test_done
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
new file mode 100755 (executable)
index 0000000..4d1c2e9
--- /dev/null
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+test_description='git checkout --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+       mkdir dir &&
+       echo parent > dir/foo &&
+       echo dummy > bar &&
+       git add bar dir/foo &&
+       git commit -m initial &&
+       test_tick &&
+       test_commit second dir/foo head &&
+       set_and_save_state bar bar_work bar_index &&
+       save_head
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+       set_and_save_state dir/foo work head &&
+       (echo n; echo n) | git checkout -p &&
+       verify_saved_state bar &&
+       verify_saved_state dir/foo
+'
+
+test_expect_success 'git checkout -p' '
+       (echo n; echo y) | git checkout -p &&
+       verify_saved_state bar &&
+       verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p with staged changes' '
+       set_state dir/foo work index
+       (echo n; echo y) | git checkout -p &&
+       verify_saved_state bar &&
+       verify_state dir/foo index index
+'
+
+test_expect_success 'git checkout -p HEAD with NO staged changes: abort' '
+       set_and_save_state dir/foo work head &&
+       (echo n; echo y; echo n) | git checkout -p HEAD &&
+       verify_saved_state bar &&
+       verify_saved_state dir/foo
+'
+
+test_expect_success 'git checkout -p HEAD with NO staged changes: apply' '
+       (echo n; echo y; echo y) | git checkout -p HEAD &&
+       verify_saved_state bar &&
+       verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p HEAD with change already staged' '
+       set_state dir/foo index index
+       # the third n is to get out in case it mistakenly does not apply
+       (echo n; echo y; echo n) | git checkout -p HEAD &&
+       verify_saved_state bar &&
+       verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p HEAD^' '
+       # the third n is to get out in case it mistakenly does not apply
+       (echo n; echo y; echo n) | git checkout -p HEAD^ &&
+       verify_saved_state bar &&
+       verify_state dir/foo parent parent
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success 'path limiting works: dir' '
+       set_state dir/foo work head &&
+       (echo y; echo n) | git checkout -p dir &&
+       verify_saved_state bar &&
+       verify_state dir/foo head head
+'
+
+test_expect_success 'path limiting works: -- dir' '
+       set_state dir/foo work head &&
+       (echo y; echo n) | git checkout -p -- dir &&
+       verify_saved_state bar &&
+       verify_state dir/foo head head
+'
+
+test_expect_success 'path limiting works: HEAD^ -- dir' '
+       # the third n is to get out in case it mistakenly does not apply
+       (echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
+       verify_saved_state bar &&
+       verify_state dir/foo parent parent
+'
+
+test_expect_success 'path limiting works: foo inside dir' '
+       set_state dir/foo work head &&
+       # the third n is to get out in case it mistakenly does not apply
+       (echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
+       verify_saved_state bar &&
+       verify_state dir/foo head head
+'
+
+test_expect_success 'none of this moved HEAD' '
+       verify_saved_head
+'
+
+test_done
diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh
new file mode 100755 (executable)
index 0000000..fc1e379
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='ls-files --exclude does not affect index files'
+. ./test-lib.sh
+
+test_expect_success 'create repo with file' '
+       echo content >file &&
+       git add file &&
+       git commit -m file &&
+       echo modification >file
+'
+
+check_output() {
+test_expect_success "ls-files output contains file ($1)" "
+       echo '$2' >expect &&
+       git ls-files --exclude-standard --$1 >output &&
+       test_cmp expect output
+"
+}
+
+check_all_output() {
+       check_output 'cached' 'file'
+       check_output 'modified' 'file'
+}
+
+check_all_output
+test_expect_success 'add file to gitignore' '
+       echo file >.gitignore
+'
+check_all_output
+
+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
 
 '
diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh
new file mode 100755 (executable)
index 0000000..7f41607
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='merge-recursive backend test'
+
+. ./test-lib.sh
+
+#         A      <- create some files
+#        / \
+#       B   C    <- cause rename/delete conflicts between B and C
+#      /     \
+#     |\     /|
+#     | D   E |
+#     |  \ /  |
+#     |   X   |
+#     |  / \  |
+#     | /   \ |
+#     |/     \|
+#     F       G  <- merge E into B, D into C
+#      \     /
+#       \   /
+#        \ /
+#         H      <- recursive merge crashes
+#
+
+# initialize
+test_expect_success 'setup repo with criss-cross history' '
+       mkdir data &&
+
+       # create a bunch of files
+       n=1 &&
+       while test $n -le 10
+       do
+               echo $n > data/$n &&
+               n=$(($n+1)) ||
+               break
+       done &&
+
+       # check them in
+       git add data &&
+       git commit -m A &&
+       git branch A &&
+
+       # a file in one branch
+       git checkout -b B A &&
+       git rm data/9 &&
+       git add data &&
+       git commit -m B &&
+
+       # with a branch off of it
+       git branch D &&
+
+       # put some commits on D
+       git checkout D &&
+       echo testD > data/testD &&
+       git add data &&
+       git commit -m D &&
+
+       # back up to the top, create another branch and cause
+       # a rename conflict with the file we deleted earlier
+       git checkout -b C A &&
+       git mv data/9 data/new-9 &&
+       git add data &&
+       git commit -m C &&
+
+       # with a branch off of it
+       git branch E &&
+
+       # put a commit on E
+       git checkout E &&
+       echo testE > data/testE &&
+       git add data &&
+       git commit -m E &&
+
+       # now, merge E into B
+       git checkout B &&
+       test_must_fail git merge E &&
+       # force-resolve
+       git add data &&
+       git commit -m F &&
+       git branch F &&
+
+       # and merge D into C
+       git checkout C &&
+       test_must_fail git merge D &&
+       # force-resolve
+       git add data &&
+       git commit -m G &&
+       git branch G
+'
+
+test_expect_success 'recursive merge between F and G, causes segfault' '
+       git merge F
+'
+
+test_done
index 7fe4a6ecb05df0fbfb825fbb08207f7672e1775f..0a5d5e669fac2e3be513df4f27c1f0a6560796db 100755 (executable)
@@ -56,4 +56,12 @@ test_expect_success 'show-branch with more than 8 branches' '
 
 '
 
+test_expect_success 'show-branch with showbranch.default' '
+       for i in $numbers; do
+               git config --add showbranch.default branch$i
+       done &&
+       git show-branch >out &&
+       test_cmp expect out
+'
+
 test_done
index 6e391a370208c9a0a6e73facc87f6a0e5081b929..4e6a44b623c456dc85f9daa6c4b5b1f0789c93c5 100755 (executable)
@@ -3,9 +3,10 @@
 # Copyright (c) 2005 Amos Waterland
 #
 
-test_description='git rebase should not destroy author information
+test_description='git rebase assorted tests
 
-This test runs git rebase and checks that the author information is not lost.
+This test runs git rebase and checks that the author information is not lost
+among other things.
 '
 . ./test-lib.sh
 
@@ -41,9 +42,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 >out &&
+     grep "Current branch my-topic-branch is up to date" out
+'
+
+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 >out &&
+     grep "Current branch my-topic-branch is up to date" out
+'
+
+test_expect_success 'rebase fast-forward to master' '
+     git checkout my-topic-branch^ &&
+     git rebase my-topic-branch >out &&
+     grep "Fast-forwarded HEAD to my-topic-branch" out
+'
+
 test_expect_success \
     'the rebase operation should not have destroyed author information' \
     '! (git log | grep "Author:" | grep "<>")'
@@ -95,4 +127,32 @@ test_expect_success 'Show verbose error when HEAD could not be detached' '
      grep "Untracked working tree file .B. would be overwritten" output.err
 '
 
+test_expect_success 'rebase -q is quiet' '
+     rm B &&
+     git checkout -b quiet topic &&
+     git rebase -q master > output.out 2>&1 &&
+     test ! -s output.out
+'
+
+q_to_cr () {
+       tr Q '\015'
+}
+
+test_expect_success 'Rebase a commit that sprinkles CRs in' '
+       (
+               echo "One"
+               echo "TwoQ"
+               echo "Three"
+               echo "FQur"
+               echo "Five"
+       ) | q_to_cr >CR &&
+       git add CR &&
+       test_tick &&
+       git commit -a -m "A file with a line with CR" &&
+       git tag file-with-cr &&
+       git checkout HEAD^0 &&
+       git rebase --onto HEAD^^ HEAD^ &&
+       git diff --exit-code file-with-cr:CR HEAD:CR
+'
+
 test_done
index c32ff6682b932b3d9b270de7260a2671d8d07403..4cae019521f988fe6a3eea0902d35dbbdae32dc8 100755 (executable)
@@ -10,7 +10,7 @@ that the result still makes sense.
 '
 . ./test-lib.sh
 
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
 
 set_fake_editor
 
@@ -119,11 +119,11 @@ index e69de29..00750ed 100644
 EOF
 
 cat > expect2 << EOF
-<<<<<<< HEAD:file1
+<<<<<<< HEAD
 2
 =======
 3
->>>>>>> b7ca976... G:file1
+>>>>>>> b7ca976... G
 EOF
 
 test_expect_success 'stop on conflicting pick' '
index e6c832780fbe00afe5c513f0f5e4c87d7020e2a4..297d165476b93e18b18bf42bc81f4740cf18db9f 100755 (executable)
@@ -71,7 +71,7 @@ test_expect_success 'rebase -p fakes interactive rebase' '
        git fetch &&
        git rebase -p origin/topic &&
        test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge commit" | wc -l)
+       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote branch " | wc -l)
        )
 '
 
index 6533505218a51db369d03357603c2ad1926ce448..14a23cd8726fbb33df480133ed0466177916b7fb 100755 (executable)
@@ -10,7 +10,7 @@ a merge to before the merge.
 '
 . ./test-lib.sh
 
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
 
 set_fake_editor
 
diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh
new file mode 100755 (executable)
index 0000000..ee0a6cc
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Greg Price
+#
+
+test_description='git rebase -p should respect --onto
+
+In a rebase with --onto, we should rewrite all the commits that
+aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM.
+'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+# Set up branches like this:
+# A1---B1---E1---F1---G1
+#  \    \             /
+#   \    \--C1---D1--/
+#    H1
+
+test_expect_success 'setup' '
+       test_commit A1 &&
+       test_commit B1 &&
+       test_commit C1 &&
+       test_commit D1 &&
+       git reset --hard B1 &&
+       test_commit E1 &&
+       test_commit F1 &&
+       test_merge G1 D1 &&
+       git reset --hard A1 &&
+       test_commit H1
+'
+
+# Now rebase merge G1 from both branches' base B1, both should move:
+# A1---B1---E1---F1---G1
+#  \    \             /
+#   \    \--C1---D1--/
+#    \
+#     H1---E2---F2---G2
+#      \             /
+#       \--C2---D2--/
+
+test_expect_success 'rebase from B1 onto H1' '
+       git checkout G1 &&
+       git rebase -p --onto H1 B1 &&
+       test "$(git rev-parse HEAD^1^1^1)" = "$(git rev-parse H1)" &&
+       test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse H1)"
+'
+
+# On the other hand if rebase from E1 which is within one branch,
+# then the other branch stays:
+# A1---B1---E1---F1---G1
+#  \    \             /
+#   \    \--C1---D1--/
+#    \             \
+#     H1-----F3-----G3
+
+test_expect_success 'rebase from E1 onto H1' '
+       git checkout G1 &&
+       git rebase -p --onto H1 E1 &&
+       test "$(git rev-parse HEAD^1^1)" = "$(git rev-parse H1)" &&
+       test "$(git rev-parse HEAD^2)" = "$(git rev-parse D1)"
+'
+
+# And the same if we rebase from a commit in the second-parent branch.
+# A1---B1---E1---F1----G1
+#  \    \          \   /
+#   \    \--C1---D1-\-/
+#    \               \
+#     H1------D3------G4
+
+test_expect_success 'rebase from C1 onto H1' '
+       git checkout G1 &&
+       git rev-list --first-parent --pretty=oneline C1..G1 &&
+       git rebase -p --onto H1 C1 &&
+       test "$(git rev-parse HEAD^2^1)" = "$(git rev-parse H1)" &&
+       test "$(git rev-parse HEAD^1)" = "$(git rev-parse F1)"
+'
+
+test_done
index 9aaeabd972ffd3009fb62b9884516cc876bbb35f..e51e505a9fb902ec7d4cedfa32052f03a04e612e 100755 (executable)
@@ -17,11 +17,11 @@ test_expect_success setup '
 
 '
 
-test_expect_code 1 'cherry-pick an empty commit' '
-
-       git checkout master &&
-       git cherry-pick empty-branch
-
+test_expect_success 'cherry-pick an empty commit' '
+       git checkout master && {
+               git cherry-pick empty-branch
+               test "$?" = 1
+       }
 '
 
 test_expect_success 'index lockfile was removed' '
index 050de42ef4148a730c30520ccaad5e9871e536bd..85eb0fbf96a65ad958422da02ca4975fe687da95 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 POSIXPERM '--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
index dfc65601aa2171bfc9321753a3d90db112d81f72..687bd7ab53397d3bc9d5150bc7cd290efa8ebdbc 100755 (executable)
@@ -138,6 +138,20 @@ test_expect_success 'real edit works' '
        test_cmp expected output
 '
 
+test_expect_success 'skip files similarly as commit -a' '
+       git reset &&
+       echo file >.gitignore &&
+       echo changed >file &&
+       echo y | git add -p file &&
+       git diff >output &&
+       git reset &&
+       git commit -am commit &&
+       git diff >expected &&
+       test_cmp expected output &&
+       git reset --hard HEAD^
+'
+rm -f .gitignore
+
 if test "$(git config --bool core.filemode)" = false
 then
        say 'skipping filemode tests (filesystem does not properly support modes)'
@@ -163,6 +177,55 @@ test_expect_success FILEMODE 'stage mode but not hunk' '
        git diff          file | grep "+content"
 '
 
+
+test_expect_success FILEMODE 'stage mode and hunk' '
+       git reset --hard &&
+       echo content >>file &&
+       chmod +x file &&
+       printf "y\\ny\\n" | git add -p &&
+       git diff --cached file | grep "new mode" &&
+       git diff --cached file | grep "+content" &&
+       test -z "$(git diff file)"
+'
+
 # end of tests disabled when filemode is not usable
 
+test_expect_success 'setup again' '
+       git reset --hard &&
+       test_chmod +x file &&
+       echo content >>file
+'
+
+# Write the patch file with a new line at the top and bottom
+cat >patch <<EOF
+index 180b47c..b6f2c08 100644
+--- a/file
++++ b/file
+@@ -1,2 +1,4 @@
++firstline
+ baseline
+ content
++lastline
+EOF
+# Expected output, similar to the patch but w/ diff at the top
+cat >expected <<EOF
+diff --git a/file b/file
+index b6f2c08..61b9053 100755
+--- a/file
++++ b/file
+@@ -1,2 +1,4 @@
++firstline
+ baseline
+ content
++lastline
+EOF
+# Test splitting the first patch, then adding both
+test_expect_success 'add first line works' '
+       git commit -am "clear local changes" &&
+       git apply patch &&
+       (echo s; echo y; echo y) | git add -p file &&
+       git diff --cached > diff &&
+       test_cmp expected diff
+'
+
 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
similarity index 100%
rename from t/t3900/ISO-8859-1.txt
rename to t/t3900/ISO8859-1.txt
similarity index 100%
rename from t/t3900/EUCJP.txt
rename to t/t3900/eucJP.txt
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 7484cbede6ccd3ecb56a3ebff734740cb543c0a4..5514f74b30aa74fe2bf214e90f9ad8f4da2876e4 100755 (executable)
@@ -177,4 +177,46 @@ test_expect_success 'stash branch' '
        test 0 = $(git stash list | wc -l)
 '
 
+test_expect_success 'apply -q is quiet' '
+       echo foo > file &&
+       git stash &&
+       git stash apply -q > output.out 2>&1 &&
+       test ! -s output.out
+'
+
+test_expect_success 'save -q is quiet' '
+       git stash save --quiet > output.out 2>&1 &&
+       test ! -s output.out
+'
+
+test_expect_success 'pop -q is quiet' '
+       git stash pop -q > output.out 2>&1 &&
+       test ! -s output.out
+'
+
+test_expect_success 'drop -q is quiet' '
+       git stash &&
+       git stash drop -q > output.out 2>&1 &&
+       test ! -s output.out
+'
+
+test_expect_success 'stash -k' '
+       echo bar3 > file &&
+       echo bar4 > file2 &&
+       git add file2 &&
+       git stash -k &&
+       test bar,bar4 = $(cat file),$(cat file2)
+'
+
+test_expect_success 'stash --invalid-option' '
+       echo bar5 > file &&
+       echo bar6 > file2 &&
+       git add file2 &&
+       test_must_fail git stash --invalid-option &&
+       test_must_fail git stash save --invalid-option &&
+       test bar5,bar6 = $(cat file),$(cat file2) &&
+       git stash -- -message-starting-with-dash &&
+       test bar,bar2 = $(cat file),$(cat file2)
+'
+
 test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
new file mode 100755 (executable)
index 0000000..f37e3bc
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='git checkout --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+       mkdir dir &&
+       echo parent > dir/foo &&
+       echo dummy > bar &&
+       git add bar dir/foo &&
+       git commit -m initial &&
+       test_tick &&
+       test_commit second dir/foo head &&
+       echo index > dir/foo &&
+       git add dir/foo &&
+       set_and_save_state bar bar_work bar_index &&
+       save_head
+'
+
+# note: bar sorts before dir, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+       set_state dir/foo work index
+       (echo n; echo n) | test_must_fail git stash save -p &&
+       verify_state dir/foo work index &&
+       verify_saved_state bar
+'
+
+test_expect_success 'git stash -p' '
+       (echo n; echo y) | git stash save -p &&
+       verify_state dir/foo head index &&
+       verify_saved_state bar &&
+       git reset --hard &&
+       git stash apply &&
+       verify_state dir/foo work head &&
+       verify_state bar dummy dummy
+'
+
+test_expect_success 'git stash -p --no-keep-index' '
+       set_state dir/foo work index &&
+       set_state bar bar_work bar_index &&
+       (echo n; echo y) | git stash save -p --no-keep-index &&
+       verify_state dir/foo head head &&
+       verify_state bar bar_work dummy &&
+       git reset --hard &&
+       git stash apply --index &&
+       verify_state dir/foo work index &&
+       verify_state bar dummy bar_index
+'
+
+test_expect_success 'none of this moved HEAD' '
+       verify_saved_head
+'
+
+test_done
index 42072d724ef67c51e6a9adcf9bf8e3ca1757e053..11502b750997aba0ecfb6aa5b07c9aad1c45d592 100755 (executable)
@@ -9,32 +9,36 @@ test_description='Rename interaction with pathspec.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
-test_expect_success \
-    'prepare reference tree' \
-    'mkdir path0 path1 &&
-     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
-     git update-index --add path0/COPYING &&
-    tree=$(git write-tree) &&
-    echo $tree'
-
-test_expect_success \
-    'prepare work tree' \
-    'cp path0/COPYING path1/COPYING &&
-     git update-index --add --remove path0/COPYING path1/COPYING'
+test_expect_success 'prepare reference tree' '
+       mkdir path0 path1 &&
+       cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+       git update-index --add path0/COPYING &&
+       tree=$(git write-tree) &&
+       echo $tree
+'
+
+test_expect_success 'prepare work tree' '
+       cp path0/COPYING path1/COPYING &&
+       git update-index --add --remove path0/COPYING path1/COPYING
+'
 
 # In the tree, there is only path0/COPYING.  In the cache, path0 and
 # path1 both have COPYING and the latter is a copy of path0/COPYING.
 # Comparing the full tree with cache should tell us so.
 
-git diff-index -C --find-copies-harder $tree >current
-
 cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100  path0/COPYING   path1/COPYING
 EOF
 
-test_expect_success \
-    'validate the result (#1)' \
-    'compare_diff_raw current expected'
+test_expect_success 'copy detection' '
+       git diff-index -C --find-copies-harder $tree >current &&
+       compare_diff_raw current expected
+'
+
+test_expect_success 'copy detection, cached' '
+       git diff-index -C --find-copies-harder --cached $tree >current &&
+       compare_diff_raw current expected
+'
 
 # In the tree, there is only path0/COPYING.  In the cache, path0 and
 # path1 both have COPYING and the latter is a copy of path0/COPYING.
@@ -42,49 +46,45 @@ test_expect_success \
 # path1/COPYING suddenly appearing from nowhere, not detected as
 # a copy from path0/COPYING.
 
-git diff-index -C $tree path1 >current
-
 cat >expected <<\EOF
 :000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A     path1/COPYING
 EOF
 
-test_expect_success \
-    'validate the result (#2)' \
-    'compare_diff_raw current expected'
-
-test_expect_success \
-    'tweak work tree' \
-    'rm -f path0/COPYING &&
-     git update-index --remove path0/COPYING'
+test_expect_success 'copy, limited to a subtree' '
+       git diff-index -C --find-copies-harder $tree path1 >current &&
+       compare_diff_raw current expected
+'
 
+test_expect_success 'tweak work tree' '
+       rm -f path0/COPYING &&
+       git update-index --remove path0/COPYING
+'
 # In the tree, there is only path0/COPYING.  In the cache, path0 does
 # not have COPYING anymore and path1 has COPYING which is a copy of
 # path0/COPYING.  Showing the full tree with cache should tell us about
 # the rename.
 
-git diff-index -C $tree >current
-
 cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100  path0/COPYING   path1/COPYING
 EOF
 
-test_expect_success \
-    'validate the result (#3)' \
-    'compare_diff_raw current expected'
+test_expect_success 'rename detection' '
+       git diff-index -C --find-copies-harder $tree >current &&
+       compare_diff_raw current expected
+'
 
 # In the tree, there is only path0/COPYING.  In the cache, path0 does
 # not have COPYING anymore and path1 has COPYING which is a copy of
 # path0/COPYING.  When we say we care only about path1, we should just
 # see path1/COPYING appearing from nowhere.
 
-git diff-index -C $tree path1 >current
-
 cat >expected <<\EOF
 :000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A     path1/COPYING
 EOF
 
-test_expect_success \
-    'validate the result (#4)' \
-    'compare_diff_raw current expected'
+test_expect_success 'rename, limited to a subtree' '
+       git diff-index -C --find-copies-harder $tree path1 >current &&
+       compare_diff_raw current expected
+'
 
 test_done
index 8b33321f8c44dc7f8a08fa144bca4f10444ceaa1..8e3694ed5b80a87602d6533f0aa28307bd7b3d1b 100755 (executable)
@@ -207,6 +207,7 @@ log --root --cc --patch-with-stat --summary master
 log -SF master
 log -SF -p master
 log --decorate --all
+log --decorate=full --all
 
 rev-list --parents HEAD
 rev-list --children HEAD
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
new file mode 100644 (file)
index 0000000..d155e0b
--- /dev/null
@@ -0,0 +1,34 @@
+$ git log --decorate=full --all
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/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)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
index 12da8ac07de4423cf4736aced96bab8150f83ef0..fd7c3e64396b4ea57c3b03d2e120580205263462 100644 (file)
@@ -1,12 +1,12 @@
 $ git log --decorate --all
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, 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 11061ddd5bd122a1e180509c018e1b8bea42ddc3..531f5b795c197d942223db3cdc2d2030208e9beb 100755 (executable)
@@ -493,16 +493,26 @@ test_expect_success 'format-patch from a subdirectory (2)' '
 '
 
 test_expect_success 'format-patch from a subdirectory (3)' '
-       here="$TEST_DIRECTORY/$test" &&
        rm -f 0* &&
        filename=$(
                rm -rf sub &&
                mkdir -p sub/dir &&
                cd sub/dir &&
-               git format-patch -1 -o "$here"
+               git format-patch -1 -o "$TRASH_DIRECTORY"
        ) &&
        basename=$(expr "$filename" : ".*/\(.*\)") &&
        test -f "$basename"
 '
 
+test_expect_success 'format-patch --in-reply-to' '
+       git format-patch -1 --stdout --in-reply-to "baz@foo.bar" > patch8 &&
+       grep "^In-Reply-To: <baz@foo.bar>" patch8 &&
+       grep "^References: <baz@foo.bar>" patch8
+'
+
+test_expect_success 'format-patch --signoff' '
+       git format-patch -1 --signoff --stdout |
+       grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+'
+
 test_done
index 6d13da30dad5a78fb17a01e86ef33072ea9e6250..8dd147d78f6ee6a4be4b1af235ed35af19b273df 100755 (executable)
@@ -362,10 +362,17 @@ test_expect_success 'line numbers in --check output are correct' '
 
 '
 
-test_expect_success 'checkdiff detects trailing blank lines' '
+test_expect_success 'checkdiff detects new trailing blank lines (1)' '
        echo "foo();" >x &&
        echo "" >>x &&
-       git diff --check | grep "ends with blank"
+       git diff --check | grep "new blank line"
+'
+
+test_expect_success 'checkdiff detects new trailing blank lines (2)' '
+       { echo a; echo b; echo; echo; } >x &&
+       git add x &&
+       { echo a; echo; echo; echo; echo; } >x &&
+       git diff --check | grep "new blank line"
 '
 
 test_expect_success 'checkdiff allows new blank lines' '
index 84a1fe31151c2af38554eaca8f03e2c1e2e7848f..3a3663fbcb4795a9e3755fe831c89a7e312f659e 100755 (executable)
@@ -165,7 +165,7 @@ test_expect_success 'trailing empty lines (1)' '
 
        rm -f .gitattributes &&
        test_must_fail git diff --check >output &&
-       grep "ends with blank lines." output &&
+       grep "new blank line at" output &&
        grep "trailing whitespace" output
 
 '
@@ -190,4 +190,13 @@ test_expect_success 'do not color trailing cr in context' '
 
 '
 
+test_expect_success 'color new trailing blank lines' '
+       { echo a; echo b; echo; echo; } >x &&
+       git add x &&
+       { echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&
+       git diff --color x >output &&
+       cnt=$(grep "${blue_grep}" output | wc -l) &&
+       test $cnt = 2
+'
+
 test_done
index 0720001281db6aeb5a3b6bb46cd6914ad7d78d33..a7602cf923d95a51643753ce44fa65cf406aba9c 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
@@ -157,7 +166,7 @@ test_expect_success 'diff --cached' '
        git update-index --assume-unchanged file &&
        echo second >file &&
        git diff --cached >actual &&
-       test_cmp ../t4020/diff.NUL actual
+       test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
 '
 
 test_done
index 390af2389f3b9559fcfebe1e21a24317562ab7af..709b3231ca8d9da631727b4aadfb2f46049d37e9 100755 (executable)
@@ -86,6 +86,13 @@ test_expect_success 'format.numbered && --no-numbered' '
 
 '
 
+test_expect_success 'format.numbered && --keep-subject' '
+
+       git format-patch --keep-subject --stdout HEAD^ >patch4a &&
+       grep "^Subject: Third" patch4a
+
+'
+
 test_expect_success 'format.numbered = auto' '
 
        git config format.numbered auto
@@ -108,4 +115,10 @@ test_expect_success 'format.numbered = auto && --no-numbered' '
 
 '
 
+test_expect_success '--start-number && --numbered' '
+
+       git format-patch --start-number 3 --numbered --stdout HEAD~1 > patch8 &&
+       grep "^Subject: \[PATCH 3/3\]" patch8
+'
+
 test_done
diff --git a/t/t4037-diff-r-t-dirs.sh b/t/t4037-diff-r-t-dirs.sh
new file mode 100755 (executable)
index 0000000..f5ce3b2
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='diff -r -t shows directory additions and deletions'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       mkdir dc dr dt &&
+       >dc/1 &&
+       >dr/2 &&
+       >dt/3 &&
+       >fc &&
+       >fr &&
+       >ft &&
+       git add . &&
+       test_tick &&
+       git commit -m initial &&
+
+       rm -fr dt dr ft fr &&
+       mkdir da ft &&
+       for p in dc/1 da/4 dt ft/5 fc
+       do
+               echo hello >$p || exit
+       done &&
+       git add -u &&
+       git add . &&
+       test_tick &&
+       git commit -m second
+'
+
+cat >expect <<\EOF
+A      da
+A      da/4
+M      dc
+M      dc/1
+D      dr
+D      dr/2
+A      dt
+D      dt
+D      dt/3
+M      fc
+D      fr
+D      ft
+A      ft
+A      ft/5
+EOF
+
+test_expect_success verify '
+       git diff-tree -r -t --name-status HEAD^ HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
new file mode 100755 (executable)
index 0000000..2cf7e01
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='combined diff'
+
+. ./test-lib.sh
+
+setup_helper () {
+       one=$1 branch=$2 side=$3 &&
+
+       git branch $side $branch &&
+       for l in $one two three fyra
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m $branch &&
+       git checkout $side &&
+       for l in $one two three quatro
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m $side &&
+       test_must_fail git merge $branch &&
+       for l in $one three four
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m "merge $branch into $side"
+}
+
+verify_helper () {
+       it=$1 &&
+
+       # Ignore lines that were removed only from the other parent
+       sed -e '
+               1,/^@@@/d
+               /^ -/d
+               s/^\(.\)./\1/
+       ' "$it" >"$it.actual.1" &&
+       sed -e '
+               1,/^@@@/d
+               /^- /d
+               s/^.\(.\)/\1/
+       ' "$it" >"$it.actual.2" &&
+
+       git diff "$it^" "$it" -- | sed -e '1,/^@@/d' >"$it.expect.1" &&
+       test_cmp "$it.expect.1" "$it.actual.1" &&
+
+       git diff "$it^2" "$it" -- | sed -e '1,/^@@/d' >"$it.expect.2" &&
+       test_cmp "$it.expect.2" "$it.actual.2"
+}
+
+test_expect_success setup '
+       >file &&
+       git add file &&
+       test_tick &&
+       git commit -m initial &&
+
+       git branch withone &&
+       git branch sansone &&
+
+       git checkout withone &&
+       setup_helper one withone sidewithone &&
+
+       git checkout sansone &&
+       setup_helper "" sansone sidesansone
+'
+
+test_expect_success 'check combined output (1)' '
+       git show sidewithone -- >sidewithone &&
+       verify_helper sidewithone
+'
+
+test_expect_failure 'check combined output (2)' '
+       git show sidesansone -- >sidesansone &&
+       verify_helper sidesansone
+'
+
+test_done
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
new file mode 100755 (executable)
index 0000000..9d9498b
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='diff with assume-unchanged entries'
+
+. ./test-lib.sh
+
+# external diff has been tested in t4020-diff-external.sh
+
+test_expect_success 'setup' '
+       echo zero > zero &&
+       git add zero &&
+       git commit -m zero &&
+       echo one > one &&
+       echo two > two &&
+       git add one two &&
+       git commit -m onetwo &&
+       git update-index --assume-unchanged one &&
+       echo borked >> one &&
+       test "$(git ls-files -v one)" = "h one"
+'
+
+test_expect_success 'diff-index does not examine assume-unchanged entries' '
+       git diff-index HEAD^ -- one | grep -q 5626abf0f72e58d7a153368ba57db4c673c0e171
+'
+
+test_expect_success 'diff-files does not examine assume-unchanged entries' '
+       rm one &&
+       test -z "$(git diff-files -- one)"
+'
+
+test_done
diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh
new file mode 100755 (executable)
index 0000000..484654d
--- /dev/null
@@ -0,0 +1,185 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Giuseppe Bilotta
+#
+
+test_description='git-apply --ignore-whitespace.
+
+'
+. ./test-lib.sh
+
+# This primes main.c file that indents without using HT at all.
+# Various patches with HT and other spaces are attempted in the test.
+
+cat > patch1.patch <<\EOF
+diff --git a/main.c b/main.c
+new file mode 100644
+--- /dev/null
++++ b/main.c
+@@ -0,0 +1,22 @@
++#include <stdio.h>
++
++void print_int(int num);
++int func(int num);
++
++int main() {
++       int i;
++
++       for (i = 0; i < 10; i++) {
++               print_int(func(i)); /* stuff */
++       }
++
++       return 0;
++}
++
++int func(int num) {
++       return num * num;
++}
++
++void print_int(int num) {
++       printf("%d", num);
++}
+EOF
+
+# Since whitespace is very significant and we want to prevent whitespace
+# mangling when creating this test from a patch, we protect 'fixable'
+# whitespace by replacing spaces with Z and replacing them at patch
+# creation time, hence the sed trick.
+
+# This patch will fail unless whitespace differences are being ignored
+
+sed -e 's/Z/ /g' > patch2.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,6 +10,8 @@
+Z              print_int(func(i)); /* stuff */
+Z      }
+Z
++      printf("\n");
++
+Z      return 0;
+Z}
+Z
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing string at EOL. TODO: this testcase should be
+# improved by creating a line that has the same hash with and without
+# the final string.
+
+sed -e 's/Z/ /g' > patch3.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,3 +10,4 @@
+Z      for (i = 0; i < 10; i++) {
+Z              print_int(func(i));Z
++              /* stuff */
+Z      }
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing EOL at EOF.
+
+sed -e 's/Z/ /g' > patch4.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -21,1 +21,1 @@
+-      };Z
+\ No newline at end of file
++      };
+EOF
+
+# This patch will fail unless whitespace differences are being ignored.
+
+sed -e 's/Z/ /g' > patch5.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -2,2 +2,3 @@
+Z      void print_int(int num);
++      /* a comment */
+Z      int func(int num);
+EOF
+
+# And this is how the final output should be.  Patches introduce
+# HTs but the original SP indents are mostly kept.
+
+sed -e 's/T/   /g' > main.c.final <<\EOF
+#include <stdio.h>
+
+void print_int(int num);
+T/* a comment */
+int func(int num);
+
+int main() {
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               print_int(func(i)); /* stuff */
+       }
+
+Tprintf("\n");
+
+       return 0;
+}
+
+int func(int num) {
+       return num * num;
+}
+
+void print_int(int num) {
+       printf("%d", num);
+}
+EOF
+
+test_expect_success 'file creation' '
+       git-apply patch1.patch
+'
+
+test_expect_success 'patch2 fails (retab)' '
+       test_must_fail git-apply patch2.patch
+'
+
+test_expect_success 'patch2 applies with --ignore-whitespace' '
+       git-apply --ignore-whitespace patch2.patch
+'
+
+test_expect_success 'patch2 reverse applies with --ignore-space-change' '
+       git-apply -R --ignore-space-change patch2.patch
+'
+
+git config apply.ignorewhitespace change
+
+test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' '
+       git-apply patch2.patch
+'
+
+test_expect_success 'patch3 fails (missing string at EOL)' '
+       test_must_fail git-apply patch3.patch
+'
+
+test_expect_success 'patch4 fails (missing EOL at EOF)' '
+       test_must_fail git-apply patch4.patch
+'
+
+test_expect_success 'patch5 applies (leading whitespace)' '
+       git-apply patch5.patch
+'
+
+test_expect_success 'patches do not mangle whitespace' '
+       test_cmp main.c main.c.final
+'
+
+test_expect_success 're-create file (with --ignore-whitespace)' '
+       rm -f main.c &&
+       git-apply patch1.patch
+'
+
+test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
+       test_must_fail git-apply --no-ignore-whitespace patch5.patch
+'
+
+test_done
index f83322e513b96bb90e71ce39340515c6be0db186..ca26397590f3d79455c41894203fbff7bb6a9c3c 100755 (executable)
@@ -148,4 +148,117 @@ do
        done
 done
 
+create_patch () {
+       sed -e "s/_/ /" <<-\EOF
+               diff --git a/target b/target
+               index e69de29..8bd6648 100644
+               --- a/target
+               +++ b/target
+               @@ -0,0 +1,3 @@
+               +An empty line follows
+               +
+               +A line with trailing whitespace and no newline_
+               \ No newline at end of file
+       EOF
+}
+
+test_expect_success 'trailing whitespace & no newline at the end of file' '
+       >target &&
+       create_patch >patch-file &&
+       git apply --whitespace=fix patch-file &&
+       grep "newline$" target &&
+       grep "^$" target
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (1)' '
+       : these can fail depending on what we did before
+       git config --unset core.whitespace
+       rm -f .gitattributes
+
+       { echo a; echo b; echo c; } >one &&
+       git add one &&
+       { echo a; echo b; echo c; } >expect &&
+       { cat expect; echo; } >one &&
+       git diff -- one >patch &&
+
+       git checkout one &&
+       git apply --whitespace=fix patch &&
+       test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (2)' '
+       { echo a; echo b; echo c; } >one &&
+       git add one &&
+       { echo a; echo c; } >expect &&
+       { cat expect; echo; echo; } >one &&
+       git diff -- one >patch &&
+
+       git checkout one &&
+       git apply --whitespace=fix patch &&
+       test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (3)' '
+       { echo a; echo b; echo; } >one &&
+       git add one &&
+       { echo a; echo c; echo; } >expect &&
+       { cat expect; echo; echo; } >one &&
+       git diff -- one >patch &&
+
+       git checkout one &&
+       git apply --whitespace=fix patch &&
+       test_cmp expect one
+'
+
+test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
+       { echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&
+       git add one &&
+       { echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&
+       cp expect one &&
+       git diff -- one >patch &&
+
+       git checkout one &&
+       git apply --whitespace=fix patch &&
+       test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=warn' '
+       { echo a; echo b; echo c; } >one &&
+       git add one &&
+       echo >>one &&
+       cat one >expect &&
+       git diff -- one >patch &&
+
+       git checkout one &&
+       git apply --whitespace=warn patch 2>error &&
+       test_cmp expect one &&
+       grep "new blank line at EOF" error
+'
+
+test_expect_success 'blank at EOF with --whitespace=error' '
+       { echo a; echo b; echo c; } >one &&
+       git add one &&
+       cat one >expect &&
+       echo >>one &&
+       git diff -- one >patch &&
+
+       git checkout one &&
+       test_must_fail git apply --whitespace=error patch 2>error &&
+       test_cmp expect one &&
+       grep "new blank line at EOF" error
+'
+
+test_expect_success 'blank but not empty at EOF' '
+       { echo a; echo b; echo c; } >one &&
+       git add one &&
+       echo "   " >>one &&
+       cat one >expect &&
+       git diff -- one >patch &&
+
+       git checkout one &&
+       git apply --whitespace=warn patch 2>error &&
+       test_cmp expect one &&
+       grep "new blank line at EOF" error
+'
+
 test_done
diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh
new file mode 100755 (executable)
index 0000000..94373ca
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Stephen Boyd
+#
+
+test_description='git apply --build-fake-ancestor handling.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit 1 &&
+       test_commit 2 &&
+       mkdir sub &&
+       test_commit 3 sub/3 &&
+       test_commit 4
+'
+
+test_expect_success 'apply --build-fake-ancestor' '
+       git checkout 2 &&
+       echo "A" > 1.t &&
+       git diff > 1.patch &&
+       git reset --hard &&
+       git checkout 1 &&
+       git apply --build-fake-ancestor 1.ancestor 1.patch
+'
+
+test_expect_success 'apply --build-fake-ancestor in a subdirectory' '
+       git checkout 3 &&
+       echo "C" > sub/3.t &&
+       git diff > 3.patch &&
+       git reset --hard &&
+       git checkout 4 &&
+       (
+               cd sub &&
+               git apply --build-fake-ancestor 3.ancestor ../3.patch &&
+               test -f 3.ancestor
+       ) &&
+       git apply --build-fake-ancestor 3.ancestor 3.patch &&
+       test_cmp sub/3.ancestor 3.ancestor
+'
+
+test_done
diff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh
new file mode 100755 (executable)
index 0000000..bb1ffe3
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Junio C Hamano
+
+test_description='git-apply notices removal patches generated by GNU diff'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       cat <<-EOF >c &&
+       diff -ruN a/file b/file
+       --- a/file      TS0
+       +++ b/file      TS1
+       @@ -0,0 +1 @@
+       +something
+       EOF
+
+       cat <<-EOF >d &&
+       diff -ruN a/file b/file
+       --- a/file      TS0
+       +++ b/file      TS1
+       @@ -1 +0,0 @@
+       -something
+       EOF
+
+       timeWest="1982-09-16 07:00:00.000000000 -0800" &&
+        timeGMT="1982-09-16 15:00:00.000000000 +0000" &&
+       timeEast="1982-09-17 00:00:00.000000000 +0900" &&
+
+       epocWest="1969-12-31 16:00:00.000000000 -0800" &&
+        epocGMT="1970-01-01 00:00:00.000000000 +0000" &&
+       epocEast="1970-01-01 09:00:00.000000000 +0900" &&
+
+       sed -e "s/TS0/$epocWest/" -e "s/TS1/$timeWest/" <c >createWest.patch &&
+       sed -e "s/TS0/$epocEast/" -e "s/TS1/$timeEast/" <c >createEast.patch &&
+       sed -e "s/TS0/$epocGMT/" -e "s/TS1/$timeGMT/" <c >createGMT.patch &&
+
+       sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" <c >addWest.patch &&
+       sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" <c >addEast.patch &&
+       sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" <c >addGMT.patch &&
+
+       sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" <d >emptyWest.patch &&
+       sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" <d >emptyEast.patch &&
+       sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" <d >emptyGMT.patch &&
+
+       sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest/" <d >removeWest.patch &&
+       sed -e "s/TS0/$timeEast/" -e "s/TS1/$epocEast/" <d >removeEast.patch &&
+       sed -e "s/TS0/$timeGMT/" -e "s/TS1/$epocGMT/" <d >removeGMT.patch &&
+
+       echo something >something &&
+       >empty
+'
+
+for patch in *.patch
+do
+       test_expect_success "test $patch" '
+               rm -f file .git/index &&
+               case "$patch" in
+               create*)
+                       # must be able to create
+                       git apply --index $patch &&
+                       test_cmp file something &&
+                       # must notice the file is already there
+                       >file &&
+                       git add file &&
+                       test_must_fail git apply $patch
+                       ;;
+               add*)
+                       # must be able to create or patch
+                       git apply $patch &&
+                       test_cmp file something &&
+                       >file &&
+                       git apply $patch &&
+                       test_cmp file something
+                       ;;
+               empty*)
+                       # must leave an empty file
+                       cat something >file &&
+                       git add file &&
+                       git apply --index $patch &&
+                       test -f file &&
+                       test_cmp empty file
+                       ;;
+               remove*)
+                       # must remove the file
+                       cat something >file &&
+                       git add file &&
+                       git apply --index $patch &&
+                       ! test -f file
+                       ;;
+               esac
+       '
+done
+
+test_done
index d6ebbaebe2c258a4694cfb709809e13d24084231..8296605234ddc82697a51de0a09b703635b4c9f4 100755 (executable)
@@ -77,6 +77,12 @@ test_expect_success setup '
        git commit -s -F msg &&
        git tag second &&
        git format-patch --stdout first >patch1 &&
+       {
+               echo "X-Fake-Field: Line One" &&
+               echo "X-Fake-Field: Line Two" &&
+               echo "X-Fake-Field: Line Three" &&
+               git format-patch --stdout first | sed -e "1d"
+       } > patch1.eml &&
        sed -n -e "3,\$p" msg >file &&
        git add file &&
        test_tick &&
@@ -108,6 +114,15 @@ test_expect_success 'am applies patch correctly' '
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
 '
 
+test_expect_success 'am applies patch e-mail not in a mbox' '
+       git checkout first &&
+       git am patch1.eml &&
+       ! test -d .git/rebase-apply &&
+       test -z "$(git diff second)" &&
+       test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+       test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
 GIT_AUTHOR_NAME="Another Thor"
 GIT_AUTHOR_EMAIL="a.thor@example.com"
 GIT_COMMITTER_NAME="Co M Miter"
@@ -180,6 +195,17 @@ test_expect_success 'am -3 falls back to 3-way merge' '
        test -z "$(git diff lorem)"
 '
 
+test_expect_success 'am -3 -q is quiet' '
+       git reset master2 --hard &&
+       sed -n -e "3,\$p" msg >file &&
+       head -n 9 msg >>file &&
+       git add file &&
+       test_tick &&
+       git commit -m "copied stuff" &&
+       git am -3 -q lorem-move.patch > output.out 2>&1 &&
+       ! test -s output.out
+'
+
 test_expect_success 'am pauses on conflict' '
        git checkout lorem2^^ &&
        test_must_fail git am lorem-move.patch &&
@@ -305,4 +331,19 @@ test_expect_success 'am into an unborn branch' '
        test "z$result" = "z$(git rev-parse first^{tree})"
 '
 
+test_expect_success 'am newline in subject' '
+       git checkout first &&
+       test_tick &&
+       sed -e "s/second/second \\\n foo/" patch1 > patchnl &&
+       git am < patchnl > output.out 2>&1 &&
+       grep "^Applying: second \\\n foo$" output.out
+'
+
+test_expect_success 'am -q is quiet' '
+       git checkout first &&
+       test_tick &&
+       git am -q < patch1 > output.out 2>&1 &&
+       ! test -s output.out
+'
+
 test_done
index 64502e2be762a82b65a72af6847d5a3b79303d45..1e952ca55b1ff146000153d720f11d1b7fa22707 100755 (executable)
@@ -149,6 +149,26 @@ test_expect_success 'git log --follow' '
 
 '
 
+cat > expect << EOF
+804a787 sixth
+394ef78 fifth
+5d31159 fourth
+EOF
+test_expect_success 'git log --no-walk <commits> sorts by commit time' '
+       git log --no-walk --oneline 5d31159 804a787 394ef78 > actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+5d31159 fourth
+804a787 sixth
+394ef78 fifth
+EOF
+test_expect_success 'git show <commits> leaves list of commits as given' '
+       git show --oneline -s 5d31159 804a787 394ef78 > actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup case sensitivity tests' '
        echo case >one &&
        test_tick &&
@@ -300,11 +320,11 @@ test_expect_success 'set up more tangled history' '
 '
 
 cat > expect <<\EOF
-*   Merge branch 'reach'
+*   Merge commit 'reach'
 |\
 | \
 |  \
-*-. \   Merge branches 'octopus-a' and 'octopus-b'
+*-. \   Merge commit 'octopus-a'; commit 'octopus-b'
 |\ \ \
 * | | | seventh
 | | * | octopus-b
@@ -324,14 +344,12 @@ cat > expect <<\EOF
 * | | |   Merge branch 'side'
 |\ \ \ \
 | * | | | side-2
-| | | |/
-| | |/|
+| | |_|/
 | |/| |
 | * | | side-1
 * | | | Second
 * | | | sixth
-| | |/
-| |/|
+| |_|/
 |/| |
 * | | fifth
 * | | fourth
index abb41b07ef1985d53c2186617e6b2fcf7e7fe033..0037f63d91a7c11f6599a99e76840a0cdbf4c779 100755 (executable)
@@ -94,6 +94,10 @@ test_expect_success 'git archive with --output' \
     'git archive --output=b4.tar HEAD &&
     test_cmp b.tar b4.tar'
 
+test_expect_success 'git archive --remote' \
+    'git archive --remote=. HEAD >b5.tar &&
+    test_cmp b.tar b5.tar'
+
 test_expect_success \
     'validate file modification time' \
     'mkdir extract &&
@@ -226,4 +230,16 @@ test_expect_success \
     'git archive --list outside of a git repo' \
     'GIT_DIR=some/non-existing/directory git archive --list'
 
+test_expect_success 'git-archive --prefix=olde-' '
+       git archive --prefix=olde- >h.tar HEAD &&
+       (
+               mkdir h &&
+               cd h &&
+               "$TAR" xf - <../h.tar
+       ) &&
+       test -d h/olde-a &&
+       test -d h/olde-a/bin &&
+       test -f h/olde-a/bin/sh
+'
+
 test_done
index e70ea94a1368dc045469808d30c717aa2b8bb158..0279d07c83b1eb6cbcf2add5fc1afd8a27d63fbd 100755 (executable)
@@ -11,18 +11,26 @@ test_expect_success 'split sample box' \
        'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
        last=`cat last` &&
        echo total is $last &&
-       test `cat last` = 13'
+       test `cat last` = 14'
+
+check_mailinfo () {
+       mail=$1 opt=$2
+       mo="$mail$opt"
+       git mailinfo -u $opt msg$mo patch$mo <$mail >info$mo &&
+       test_cmp "$TEST_DIRECTORY"/t5100/msg$mo msg$mo &&
+       test_cmp "$TEST_DIRECTORY"/t5100/patch$mo patch$mo &&
+       test_cmp "$TEST_DIRECTORY"/t5100/info$mo info$mo
+}
+
 
 for mail in `echo 00*`
 do
        test_expect_success "mailinfo $mail" '
-               git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
-               echo msg &&
-               test_cmp "$TEST_DIRECTORY"/t5100/msg$mail msg$mail &&
-               echo patch &&
-               test_cmp "$TEST_DIRECTORY"/t5100/patch$mail patch$mail &&
-               echo info &&
-               test_cmp "$TEST_DIRECTORY"/t5100/info$mail info$mail
+               check_mailinfo $mail "" &&
+               if test -f "$TEST_DIRECTORY"/t5100/msg$mail--scissors
+               then
+                       check_mailinfo $mail --scissors
+               fi
        '
 done
 
diff --git a/t/t5100/info0014 b/t/t5100/info0014
new file mode 100644 (file)
index 0000000..08566b3
--- /dev/null
@@ -0,0 +1,5 @@
+Author: Junio Hamano
+Email: junkio@cox.net
+Subject: BLAH ONE
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
diff --git a/t/t5100/info0014--scissors b/t/t5100/info0014--scissors
new file mode 100644 (file)
index 0000000..ab9c8d0
--- /dev/null
@@ -0,0 +1,5 @@
+Author: Junio C Hamano
+Email: gitster@pobox.com
+Subject: Teach mailinfo to ignore everything before -- >8 -- mark
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
diff --git a/t/t5100/msg0014 b/t/t5100/msg0014
new file mode 100644 (file)
index 0000000..62e5cd2
--- /dev/null
@@ -0,0 +1,18 @@
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/msg0014--scissors b/t/t5100/msg0014--scissors
new file mode 100644 (file)
index 0000000..259c6a4
--- /dev/null
@@ -0,0 +1,4 @@
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/patch0014 b/t/t5100/patch0014
new file mode 100644 (file)
index 0000000..124efd2
--- /dev/null
@@ -0,0 +1,64 @@
+---
+ builtin-mailinfo.c |   37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+       return 0;
+ }
++static int scissors(const struct strbuf *line)
++{
++      size_t i, len = line->len;
++      int scissors_dashes_seen = 0;
++      const char *buf = line->buf;
++
++      for (i = 0; i < len; i++) {
++              if (isspace(buf[i]))
++                      continue;
++              if (buf[i] == '-') {
++                      scissors_dashes_seen |= 02;
++                      continue;
++              }
++              if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++                      scissors_dashes_seen |= 01;
++                      i++;
++                      continue;
++              }
++              if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++                      i += 7;
++                      continue;
++              }
++              /* everything else --- not scissors */
++              break;
++      }
++      return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+       static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+               strbuf_ltrim(line);
+               if (!line->len)
+                       return 0;
+-              if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++              still_looking = check_header(line, s_hdr_data, 0);
++              if (still_looking)
+                       return 0;
+       }
++      if (scissors(line)) {
++              fseek(cmitmsg, 0L, SEEK_SET);
++              still_looking = 1;
++              return 0;
++      }
++
+       /* normalize the log message to UTF-8. */
+       if (metainfo_charset)
+               convert_to_utf8(line, charset.buf);
+-- 
+1.6.4.1
diff --git a/t/t5100/patch0014--scissors b/t/t5100/patch0014--scissors
new file mode 100644 (file)
index 0000000..124efd2
--- /dev/null
@@ -0,0 +1,64 @@
+---
+ builtin-mailinfo.c |   37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+       return 0;
+ }
++static int scissors(const struct strbuf *line)
++{
++      size_t i, len = line->len;
++      int scissors_dashes_seen = 0;
++      const char *buf = line->buf;
++
++      for (i = 0; i < len; i++) {
++              if (isspace(buf[i]))
++                      continue;
++              if (buf[i] == '-') {
++                      scissors_dashes_seen |= 02;
++                      continue;
++              }
++              if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++                      scissors_dashes_seen |= 01;
++                      i++;
++                      continue;
++              }
++              if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++                      i += 7;
++                      continue;
++              }
++              /* everything else --- not scissors */
++              break;
++      }
++      return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+       static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+               strbuf_ltrim(line);
+               if (!line->len)
+                       return 0;
+-              if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++              still_looking = check_header(line, s_hdr_data, 0);
++              if (still_looking)
+                       return 0;
+       }
++      if (scissors(line)) {
++              fseek(cmitmsg, 0L, SEEK_SET);
++              still_looking = 1;
++              return 0;
++      }
++
+       /* normalize the log message to UTF-8. */
+       if (metainfo_charset)
+               convert_to_utf8(line, charset.buf);
+-- 
+1.6.4.1
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..13fa4ae03b917148e1c9c771ba0672c001a53046 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
@@ -561,3 +561,92 @@ From: <a.u.thor@example.com> (A U Thor)
 Date: Fri, 9 Jun 2006 00:44:16 -0700
 Subject: [PATCH] a patch
 
+From nobody Mon Sep 17 00:00:00 2001
+From: Junio Hamano <junkio@cox.net>
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+Subject: Why doesn't git-am does not like >8 scissors mark?
+
+Subject: [PATCH] BLAH ONE
+
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+---
+ builtin-mailinfo.c |   37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+       return 0;
+ }
++static int scissors(const struct strbuf *line)
++{
++      size_t i, len = line->len;
++      int scissors_dashes_seen = 0;
++      const char *buf = line->buf;
++
++      for (i = 0; i < len; i++) {
++              if (isspace(buf[i]))
++                      continue;
++              if (buf[i] == '-') {
++                      scissors_dashes_seen |= 02;
++                      continue;
++              }
++              if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++                      scissors_dashes_seen |= 01;
++                      i++;
++                      continue;
++              }
++              if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++                      i += 7;
++                      continue;
++              }
++              /* everything else --- not scissors */
++              break;
++      }
++      return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+       static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+               strbuf_ltrim(line);
+               if (!line->len)
+                       return 0;
+-              if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++              still_looking = check_header(line, s_hdr_data, 0);
++              if (still_looking)
+                       return 0;
+       }
++      if (scissors(line)) {
++              fseek(cmitmsg, 0L, SEEK_SET);
++              still_looking = 1;
++              return 0;
++      }
++
+       /* normalize the log message to UTF-8. */
+       if (metainfo_charset)
+               convert_to_utf8(line, charset.buf);
+-- 
+1.6.4.1
index 5132d4130968691bb115f5fc08cd0d2fd64ce669..5f6cd4f3332f600888c402d2f1b72ddc8840b8cf 100755 (executable)
@@ -275,4 +275,13 @@ test_expect_success \
      git cat-file blob $blob_2 > /dev/null &&
      git cat-file blob $blob_3 > /dev/null'
 
+test_expect_success \
+    'corrupting header to have too small output buffer fails unpack' \
+    'create_new_pack &&
+     git prune-packed &&
+     printf "\262\001" | do_corrupt_object $blob_1 0 &&
+     test_must_fail git cat-file blob $blob_1 > /dev/null &&
+     test_must_fail git cat-file blob $blob_2 > /dev/null &&
+     test_must_fail git cat-file blob $blob_3 > /dev/null'
+
 test_done
index 55ed7c7935c007573761842cea5978a784c865b3..3c6687abecf6b74839315c5e3dc09f361154a68c 100755 (executable)
@@ -6,6 +6,17 @@
 test_description='prune'
 . ./test-lib.sh
 
+day=$((60*60*24))
+week=$(($day*7))
+
+add_blob() {
+       before=$(git count-objects | sed "s/ .*//") &&
+       BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       test -f $BLOB_FILE
+}
+
 test_expect_success setup '
 
        : > file &&
@@ -31,11 +42,7 @@ test_expect_success 'prune stale packs' '
 
 test_expect_success 'prune --expire' '
 
-       before=$(git count-objects | sed "s/ .*//") &&
-       BLOB=$(echo aleph | git hash-object -w --stdin) &&
-       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-       test -f $BLOB_FILE &&
+       add_blob &&
        git prune --expire=1.hour.ago &&
        test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
        test -f $BLOB_FILE &&
@@ -48,16 +55,12 @@ test_expect_success 'prune --expire' '
 
 test_expect_success 'gc: implicit prune --expire' '
 
-       before=$(git count-objects | sed "s/ .*//") &&
-       BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
-       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-       test -f $BLOB_FILE &&
-       test-chmtime =-$((86400*14-30)) $BLOB_FILE &&
+       add_blob &&
+       test-chmtime =-$((2*$week-30)) $BLOB_FILE &&
        git gc &&
        test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
        test -f $BLOB_FILE &&
-       test-chmtime =-$((86400*14+1)) $BLOB_FILE &&
+       test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
        git gc &&
        test $before = $(git count-objects | sed "s/ .*//") &&
        ! test -f $BLOB_FILE
@@ -114,12 +117,8 @@ test_expect_success 'prune: do not prune heads listed as an argument' '
 
 test_expect_success 'gc --no-prune' '
 
-       before=$(git count-objects | sed "s/ .*//") &&
-       BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
-       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-       test -f $BLOB_FILE &&
-       test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+       add_blob &&
+       test-chmtime =-$((5001*$day)) $BLOB_FILE &&
        git config gc.pruneExpire 2.days.ago &&
        git gc --no-prune &&
        test 1 = $(git count-objects | sed "s/ .*//") &&
@@ -140,9 +139,8 @@ test_expect_success 'gc respects gc.pruneExpire' '
 
 test_expect_success 'gc --prune=<date>' '
 
-       BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
-       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-       test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+       add_blob &&
+       test-chmtime =-$((5001*$day)) $BLOB_FILE &&
        git gc --prune=5002.days.ago &&
        test -f $BLOB_FILE &&
        git gc --prune=5000.days.ago &&
@@ -150,4 +148,18 @@ test_expect_success 'gc --prune=<date>' '
 
 '
 
+test_expect_success 'gc: prune old objects after local clone' '
+       add_blob &&
+       test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
+       git clone --no-hardlinks . aclone &&
+       (
+               cd aclone &&
+               test 1 = $(git count-objects | sed "s/ .*//") &&
+               test -f $BLOB_FILE &&
+               git gc --prune &&
+               test 0 = $(git count-objects | sed "s/ .*//") &&
+               ! test -f $BLOB_FILE
+       )
+'
+
 test_done
index c450f33f333e6f1c367f8f350dfd78f8f44a0fee..18376d66081759c6a4959a2d8bc47ca441364660 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,176 @@ 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
+       (
+               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
+'
 
-add B1 $A1
+pull_to_client 1st "B A" $((11*3))
 
-echo $ATIP > .git/refs/heads/A
-echo $BTIP > .git/refs/heads/B
-git symbolic-ref HEAD refs/heads/B
+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 1st "B A" $((11*3))
+pull_to_client 2nd "B" $((64*3))
 
-add A11 $A10
+pull_to_client 3rd "A" $((1*3))
 
-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 'clone shallow' '
+       git clone --depth 2 "file://$(pwd)/." shallow
+'
 
-pull_to_client 2nd "B" $((64*3))
+test_expect_success 'clone shallow object count' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow &&
+       grep "^in-pack: 18" count.shallow
+'
 
-pull_to_client 3rd "A" $((1*3)) # old fails
+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_expect_success "clone shallow" 'git clone --depth 2 "file://$(pwd)/." shallow'
+test_expect_success 'fsck in shallow repo' '
+       (
+               cd shallow &&
+               git fsck --full
+       )
+'
 
-(cd shallow; git count-objects -v) > count.shallow
+test_expect_success 'simple fetch in shallow repo' '
+       (
+               cd shallow &&
+               git fetch
+       )
+'
 
-test_expect_success "clone shallow object count" \
-       "test \"in-pack: 18\" = \"$(grep in-pack count.shallow)\""
+test_expect_success 'no changes expected' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow.2 &&
+       cmp count.shallow count.shallow.2
+'
 
-count_output () {
-       sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/^size-pack:/d' -e '/: 0$/d' "$1"
-}
+test_expect_success 'fetch same depth in shallow repo' '
+       (
+               cd shallow &&
+               git fetch --depth=2
+       )
+'
 
-test_expect_success "clone shallow object count (part 2)" '
-       test -z "$(count_output count.shallow)"
+test_expect_success 'no changes expected' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow.3 &&
+       cmp count.shallow count.shallow.3
 '
 
-test_expect_success "fsck in shallow repo" \
-       "(cd shallow; git fsck --full)"
+test_expect_success 'add two more' '
+       add B66 $B65 &&
+       add B67 $B66
+'
 
-#test_done; exit
+test_expect_success 'pull in shallow repo' '
+       (
+               cd shallow &&
+               git pull .. B
+       )
+'
 
-add B66 $B65
-add B67 $B66
+test_expect_success 'clone shallow object count' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow &&
+       grep "^count: 6" count.shallow
+'
 
-test_expect_success "pull in shallow repo" \
-       "(cd shallow; git pull .. B)"
+test_expect_success 'add two more (part 2)' '
+       add B68 $B67 &&
+       add B69 $B68
+'
 
-(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 'deepening pull in shallow repo' '
+       (
+               cd shallow &&
+               git pull --depth 4 .. B
+       )
+'
 
-add B68 $B67
-add B69 $B68
+test_expect_success 'clone shallow object count' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow &&
+       grep "^count: 12" count.shallow
+'
 
-test_expect_success "deepening pull in shallow repo" \
-       "(cd shallow; git pull --depth 4 .. B)"
+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: 12\" = \"$(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 "deepening fetch in shallow repo" \
-       "(cd shallow; git fetch --depth 4 .. A:A)"
+test_expect_success 'pull in shallow repo with missing merge base' '
+       (
+               cd shallow &&
+               test_must_fail git pull --depth 4 .. 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 'additional simple shallow deepenings' '
+       (
+               cd shallow &&
+               git fetch --depth=8 &&
+               git fetch --depth=10 &&
+               git fetch --depth=11
+       )
+'
 
-test_expect_success "pull in shallow repo with missing merge base" \
-       "(cd shallow && test_must_fail git pull --depth 4 .. A)"
+test_expect_success 'clone shallow object count' '
+       (
+               cd shallow &&
+               git count-objects -v
+       ) > count.shallow &&
+       grep "^count: 52" count.shallow
+'
 
 test_done
diff --git a/t/t5501-post-upload-pack.sh b/t/t5501-post-upload-pack.sh
new file mode 100755 (executable)
index 0000000..d89fb51
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='post upload-hook'
+
+. ./test-lib.sh
+
+LOGFILE=".git/post-upload-pack-log"
+
+test_expect_success setup '
+       test_commit A &&
+       test_commit B &&
+       git reset --hard A &&
+       test_commit C &&
+       git branch prev B &&
+       mkdir -p .git/hooks &&
+       {
+               echo "#!$SHELL_PATH" &&
+               echo "cat >post-upload-pack-log"
+       } >".git/hooks/post-upload-pack" &&
+       chmod +x .git/hooks/post-upload-pack
+'
+
+test_expect_success initial '
+       rm -fr sub &&
+       git init sub &&
+       (
+               cd sub &&
+               git fetch --no-tags .. prev
+       ) &&
+       want=$(sed -n "s/^want //p" "$LOGFILE") &&
+       test "$want" = "$(git rev-parse --verify B)" &&
+       ! grep "^have " "$LOGFILE" &&
+       kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+       test "$kind" = fetch
+'
+
+test_expect_success second '
+       rm -fr sub &&
+       git init sub &&
+       (
+               cd sub &&
+               git fetch --no-tags .. prev:refs/remotes/prev &&
+               git fetch --no-tags .. master
+       ) &&
+       want=$(sed -n "s/^want //p" "$LOGFILE") &&
+       test "$want" = "$(git rev-parse --verify C)" &&
+       have=$(sed -n "s/^have //p" "$LOGFILE") &&
+       test "$have" = "$(git rev-parse --verify B)" &&
+       kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+       test "$kind" = fetch
+'
+
+test_expect_success all '
+       rm -fr sub &&
+       HERE=$(pwd) &&
+       git init sub &&
+       (
+               cd sub &&
+               git clone "file://$HERE/.git" new
+       ) &&
+       sed -n "s/^want //p" "$LOGFILE" | sort >actual &&
+       git rev-parse A B C | sort >expect &&
+       test_cmp expect actual &&
+       ! grep "^have " "$LOGFILE" &&
+       kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+       test "$kind" = clone
+'
+
+test_done
index 16eadd6b68664884836976aafb6dcbb582603c09..1037a723fe74756f241346a077f4f3682dbbf45d 100755 (executable)
@@ -119,4 +119,24 @@ test_expect_success 'quickfetch should not copy from alternate' '
 
 '
 
+test_expect_success 'quickfetch should handle ~1000 refs (on Windows)' '
+
+       git gc &&
+       head=$(git rev-parse HEAD) &&
+       branchprefix="$head refs/heads/branch" &&
+       for i in 0 1 2 3 4 5 6 7 8 9; do
+               for j in 0 1 2 3 4 5 6 7 8 9; do
+                       for k in 0 1 2 3 4 5 6 7 8 9; do
+                               echo "$branchprefix$i$j$k" >> .git/packed-refs
+                       done
+               done
+       done &&
+       (
+               cd cloned &&
+               git fetch &&
+               git fetch
+       )
+
+'
+
 test_done
index 5ec668d6d8ac22f161549e5592a49bfdb0f11081..852ccb5d7d649a71eeab25eadce6940b9c2a6f63 100755 (executable)
@@ -135,7 +135,8 @@ EOF
 
 cat > test/expect << EOF
 * remote origin
-  URL: $(pwd)/one
+  Fetch URL: $(pwd)/one
+  Push  URL: $(pwd)/one
   HEAD branch: master
   Remote branches:
     master new (next fetch will store in remotes/origin)
@@ -151,7 +152,8 @@ cat > test/expect << EOF
     master pushes to master   (local out of date)
     master pushes to upstream (create)
 * remote two
-  URL: ../two
+  Fetch URL: ../two
+  Push  URL: ../three
   HEAD branch (remote HEAD is ambiguous, may be one of the following):
     another
     master
@@ -173,6 +175,7 @@ test_expect_success 'show' '
         git branch --track rebase origin/master &&
         git branch -d -r origin/master &&
         git config --add remote.two.url ../two &&
+        git config --add remote.two.pushurl ../three &&
         git config branch.rebase.rebase true &&
         git config branch.octopus.merge "topic-a topic-b topic-c" &&
         (cd ../one &&
@@ -191,7 +194,8 @@ test_expect_success 'show' '
 
 cat > test/expect << EOF
 * remote origin
-  URL: $(pwd)/one
+  Fetch URL: $(pwd)/one
+  Push  URL: $(pwd)/one
   HEAD branch: (not queried)
   Remote branches: (status not queried)
     master
@@ -494,5 +498,15 @@ test_expect_success 'remote prune to cause a dangling symref' '
        grep "dangling symref" err
 '
 
+test_expect_success 'show empty remote' '
+
+       test_create_repo empty &&
+       git clone empty empty-clone &&
+       (
+               cd empty-clone &&
+               git remote show origin
+       )
+'
+
 test_done
 
index bee3424fed770aacac6dbcdf4ba58bfd7bf5c2da..d13c806624bcc8a404be97d61500e8e1d4614c6b 100755 (executable)
@@ -9,6 +9,11 @@ test_description='Per branch config variables affects "git fetch".
 
 D=`pwd`
 
+test_bundle_object_count () {
+       git verify-pack -v "$1" >verify.out &&
+       test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | wc -l)
+}
+
 test_expect_success setup '
        echo >file original &&
        git add file &&
@@ -146,6 +151,7 @@ test_expect_success 'unbundle 1' '
        test_must_fail git fetch "$D/bundle1" master:master
 '
 
+
 test_expect_success 'bundle 1 has only 3 files ' '
        cd "$D" &&
        (
@@ -156,8 +162,7 @@ test_expect_success 'bundle 1 has only 3 files ' '
                cat
        ) <bundle1 >bundle.pack &&
        git index-pack bundle.pack &&
-       verify=$(git verify-pack -v bundle.pack) &&
-       test 4 = $(echo "$verify" | wc -l)
+       test_bundle_object_count bundle.pack 3
 '
 
 test_expect_success 'unbundle 2' '
@@ -180,7 +185,7 @@ test_expect_success 'bundle does not prerequisite objects' '
                cat
        ) <bundle3 >bundle.pack &&
        git index-pack bundle.pack &&
-       test 4 = $(git verify-pack -v bundle.pack | wc -l)
+       test_bundle_object_count bundle.pack 3
 '
 
 test_expect_success 'bundle should be able to create a full history' '
index 89649e7a9b2dc138bb9618d4e5770e76f62dc7e0..6889a53cf9bdea0aff88789f954ddf31d1eec010 100755 (executable)
@@ -122,6 +122,23 @@ test_expect_success 'fetch with insteadOf' '
        )
 '
 
+test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
+       mk_empty &&
+       (
+               TRASH=$(pwd)/ &&
+               cd testrepo &&
+               git config "url.trash/.pushInsteadOf" "$TRASH" &&
+               git config remote.up.url "$TRASH." &&
+               git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+               git fetch up &&
+
+               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+               test "z$r" = "z$the_commit" &&
+
+               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+       )
+'
+
 test_expect_success 'push without wildcard' '
        mk_empty &&
 
@@ -162,6 +179,36 @@ test_expect_success 'push with insteadOf' '
        )
 '
 
+test_expect_success 'push with pushInsteadOf' '
+       mk_empty &&
+       TRASH="$(pwd)/" &&
+       git config "url.$TRASH.pushInsteadOf" trash/ &&
+       git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+       (
+               cd testrepo &&
+               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+               test "z$r" = "z$the_commit" &&
+
+               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+       )
+'
+
+test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
+       mk_empty &&
+       TRASH="$(pwd)/" &&
+       git config "url.trash2/.pushInsteadOf" trash/ &&
+       git config remote.r.url trash/wrong &&
+       git config remote.r.pushurl "$TRASH/testrepo" &&
+       git push r refs/heads/master:refs/remotes/origin/master &&
+       (
+               cd testrepo &&
+               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+               test "z$r" = "z$the_commit" &&
+
+               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+       )
+'
+
 test_expect_success 'push with matching heads' '
 
        mk_test heads/master &&
@@ -419,6 +466,19 @@ test_expect_success 'push with config remote.*.push = HEAD' '
 git config --remove-section remote.there
 git config --remove-section branch.master
 
+test_expect_success 'push with config remote.*.pushurl' '
+
+       mk_test heads/master &&
+       git checkout master &&
+       git config remote.there.url test2repo &&
+       git config remote.there.pushurl testrepo &&
+       git push there &&
+       check_push_result $the_commit heads/master
+'
+
+# clean up the cruft left with the previous one
+git config --remove-section remote.there
+
 test_expect_success 'push with dry-run' '
 
        mk_test heads/master &&
index 725771fac167ea5aac8cf65b916c96918b0f5e0d..dd2ee842e020c23b49ed4e2070c4e31cdb7ac055 100755 (executable)
@@ -92,20 +92,47 @@ 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 'rebased upstream + fetch + pull --rebase' '
+
+       git update-ref refs/remotes/me/copy copy-orig &&
+       git reset --hard to-rebase-orig &&
+       git checkout --track -b to-rebase3 me/copy &&
+       git reset --hard to-rebase-orig &&
+       git fetch &&
+       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 &&
@@ -122,4 +149,15 @@ test_expect_success 'pull --rebase dies early with dirty working directory' '
 
 '
 
+test_expect_success 'pull --rebase works on branch yet to be born' '
+       git rev-parse master >expect &&
+       mkdir empty_repo &&
+       (cd empty_repo &&
+        git init &&
+        git pull --rebase .. master &&
+        git rev-parse HEAD >../actual
+       ) &&
+       test_cmp expect actual
+'
+
 test_done
index f5102b902a4fa0505fee13aa18d38a211cdb42cb..a696b8791b7caa44ae2bd16d6970a791f3a28d3d 100755 (executable)
@@ -30,11 +30,12 @@ test_expect_success 'fsck fails' '
        test_must_fail git fsck
 '
 
-test_expect_success 'upload-pack fails due to error in pack-objects' '
+test_expect_success 'upload-pack fails due to error in pack-objects packing' '
 
        ! echo "0032want $(git rev-parse HEAD)
 00000009done
 0000" | git upload-pack . > /dev/null 2> output.err &&
+       grep "unable to read" output.err &&
        grep "pack-objects died" output.err
 '
 
@@ -50,10 +51,22 @@ test_expect_success 'fsck fails' '
 '
 test_expect_success 'upload-pack fails due to error in rev-list' '
 
+       ! echo "0032want $(git rev-parse HEAD)
+0034shallow $(git rev-parse HEAD^)00000009done
+0000" | git upload-pack . > /dev/null 2> output.err &&
+       # pack-objects survived
+       grep "Total.*, reused" output.err &&
+       # but there was an error, which must have been in rev-list
+       grep "bad tree object" output.err
+'
+
+test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
+
        ! echo "0032want $(git rev-parse HEAD)
 00000009done
 0000" | git upload-pack . > /dev/null 2> output.err &&
-       grep "waitpid (async) failed" output.err
+       grep "bad tree object" output.err &&
+       grep "pack-objects died" output.err
 '
 
 test_expect_success 'create empty repository' '
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
new file mode 100755 (executable)
index 0000000..65d8d47
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='unpack-objects'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       mkdir pub.git &&
+       GIT_DIR=pub.git git init --bare
+       GIT_DIR=pub.git git config receive.fsckobjects true &&
+       mkdir work &&
+       (
+               cd work &&
+               git init &&
+               mkdir -p gar/bage &&
+               (
+                       cd gar/bage &&
+                       git init &&
+                       >junk &&
+                       git add junk &&
+                       git commit -m "Initial junk"
+               ) &&
+               git add gar/bage &&
+               git commit -m "Initial superproject"
+       )
+'
+
+test_expect_success push '
+       (
+               cd work &&
+               git push ../pub.git master
+       )
+'
+
+test_done
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 2335d8bc850b4b0010fcb9ac64a25d0c205e8a42..214756731baf199e6a50f9ab2380a8b4bfc0fb18 100755 (executable)
@@ -149,11 +149,13 @@ test_expect_success 'clone a void' '
        (
                cd src-0 && git init
        ) &&
-       git clone src-0 target-6 &&
+       git clone "file://$(pwd)/src-0" target-6 2>err-6 &&
+       ! grep "fatal:" err-6 &&
        (
                cd src-0 && test_commit A
        ) &&
-       git clone src-0 target-7 &&
+       git clone "file://$(pwd)/src-0" target-7 2>err-7 &&
+       ! grep "fatal:" err-7 &&
        # There is no reason to insist they are bit-for-bit
        # identical, but this test should suffice for now.
        test_cmp target-6/.git/config target-7/.git/config
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
new file mode 100755 (executable)
index 0000000..f3f9a76
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='clone --branch option'
+. ./test-lib.sh
+
+check_HEAD() {
+       echo refs/heads/"$1" >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual
+}
+
+check_file() {
+       echo "$1" >expect &&
+       test_cmp expect file
+}
+
+test_expect_success 'setup' '
+       mkdir parent &&
+       (cd parent && git init &&
+        echo one >file && git add file && git commit -m one &&
+        git checkout -b two &&
+        echo two >file && git add file && git commit -m two &&
+        git checkout master)
+'
+
+test_expect_success 'vanilla clone chooses HEAD' '
+       git clone parent clone &&
+       (cd clone &&
+        check_HEAD master &&
+        check_file one
+       )
+'
+
+test_expect_success 'clone -b chooses specified branch' '
+       git clone -b two parent clone-two &&
+       (cd clone-two &&
+        check_HEAD two &&
+        check_file two
+       )
+'
+
+test_expect_success 'clone -b sets up tracking' '
+       (cd clone-two &&
+        echo origin >expect &&
+        git config branch.two.remote >actual &&
+        echo refs/heads/two >>expect &&
+        git config branch.two.merge >>actual &&
+        test_cmp expect actual
+       )
+'
+
+test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
+       (cd clone-two &&
+        echo refs/remotes/origin/master >expect &&
+        git symbolic-ref refs/remotes/origin/HEAD >actual &&
+        test_cmp expect actual
+       )
+'
+
+test_expect_success 'clone -b with bogus branch chooses HEAD' '
+       git clone -b bogus parent clone-bogus &&
+       (cd clone-bogus &&
+        check_HEAD master &&
+        check_file one
+       )
+'
+
+test_done
index 04e4b7c5c2aa4f7c6922ebc5c9236962ab5d175c..0144d9e858d2d8bf1720331c52f1809ed36e81b0 100755 (executable)
@@ -110,6 +110,18 @@ test_expect_success 'compute merge-base (all)' \
 
 # Another set to demonstrate base between one commit and a merge
 # in the documentation.
+#
+# * C (MMC) * B (MMB) * A  (MMA)
+# * o       * o       * o
+# * o       * o       * o
+# * o       * o       * o
+# * o       | _______/
+# |         |/
+# |         * 1 (MM1)
+# | _______/
+# |/
+# * root (MMR)
+
 
 test_expect_success 'merge-base for octopus-step (setup)' '
        test_tick && git commit --allow-empty -m root && git tag MMR &&
@@ -137,6 +149,12 @@ test_expect_success 'merge-base A B C' '
        test "$MM1" = "$MB"
 '
 
+test_expect_success 'merge-base A B C using show-branch' '
+       MB=$(git show-branch --merge-base MMA MMB MMC) &&
+       MMR=$(git rev-parse --verify MMR) &&
+       test "$MMR" = "$MB"
+'
+
 test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
        git reset --hard MMR &&
        test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh
new file mode 100755 (executable)
index 0000000..8b146fb
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='--show-all --parents does not rewrite TREESAME commits'
+
+. ./test-lib.sh
+
+test_expect_success 'set up --show-all --parents test' '
+       test_commit one foo.txt &&
+       commit1=`git rev-list -1 HEAD` &&
+       test_commit two bar.txt &&
+       commit2=`git rev-list -1 HEAD` &&
+       test_commit three foo.txt &&
+       commit3=`git rev-list -1 HEAD`
+       '
+
+test_expect_success '--parents rewrites TREESAME parents correctly' '
+       echo $commit3 $commit1 > expected &&
+       echo $commit1 >> expected &&
+       git rev-list --parents HEAD -- foo.txt > actual &&
+       test_cmp expected actual
+       '
+
+test_expect_success '--parents --show-all does not rewrites TREESAME parents' '
+       echo $commit3 $commit2 > expected &&
+       echo $commit2 $commit1 >> expected &&
+       echo $commit1 >> expected &&
+       git rev-list --parents --show-all HEAD -- foo.txt > actual &&
+       test_cmp expected actual
+       '
+
+test_done
diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh
new file mode 100755 (executable)
index 0000000..27fd52b
--- /dev/null
@@ -0,0 +1,276 @@
+#!/bin/sh
+
+# There's more than one "correct" way to represent the history graphically.
+# These tests depend on the current behavior of the graphing code.  If the
+# graphing code is ever changed to draw the output differently, these tests
+# cases will need to be updated to know about the new layout.
+
+test_description='--graph and simplified history'
+
+. ./test-lib.sh
+
+test_expect_success 'set up rev-list --graph test' '
+       # 3 commits on branch A
+       test_commit A1 foo.txt &&
+       test_commit A2 bar.txt &&
+       test_commit A3 bar.txt &&
+       git branch -m master A &&
+
+       # 2 commits on branch B, started from A1
+       git checkout -b B A1 &&
+       test_commit B1 foo.txt &&
+       test_commit B2 abc.txt &&
+
+       # 2 commits on branch C, started from A2
+       git checkout -b C A2 &&
+       test_commit C1 xyz.txt &&
+       test_commit C2 xyz.txt &&
+
+       # Octopus merge B and C into branch A
+       git checkout A &&
+       git merge B C &&
+       git tag A4
+
+       test_commit A5 bar.txt &&
+
+       # More commits on C, then merge C into A
+       git checkout C &&
+       test_commit C3 foo.txt &&
+       test_commit C4 bar.txt &&
+       git checkout A &&
+       git merge -s ours C &&
+       git tag A6
+
+       test_commit A7 bar.txt &&
+
+       # Store commit names in variables for later use
+       A1=$(git rev-parse --verify A1) &&
+       A2=$(git rev-parse --verify A2) &&
+       A3=$(git rev-parse --verify A3) &&
+       A4=$(git rev-parse --verify A4) &&
+       A5=$(git rev-parse --verify A5) &&
+       A6=$(git rev-parse --verify A6) &&
+       A7=$(git rev-parse --verify A7) &&
+       B1=$(git rev-parse --verify B1) &&
+       B2=$(git rev-parse --verify B2) &&
+       C1=$(git rev-parse --verify C1) &&
+       C2=$(git rev-parse --verify C2) &&
+       C3=$(git rev-parse --verify C3) &&
+       C4=$(git rev-parse --verify C4)
+       '
+
+test_expect_success '--graph --all' '
+       rm -f expected &&
+       echo "* $A7" >> expected &&
+       echo "*   $A6" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "| * $C3" >> expected &&
+       echo "* | $A5" >> expected &&
+       echo "| |     " >> expected &&
+       echo "|  \\    " >> expected &&
+       echo "*-. \\   $A4" >> expected &&
+       echo "|\\ \\ \\  " >> expected &&
+       echo "| | |/  " >> expected &&
+       echo "| | * $C2" >> expected &&
+       echo "| | * $C1" >> expected &&
+       echo "| * | $B2" >> expected &&
+       echo "| * | $B1" >> expected &&
+       echo "* | | $A3" >> expected &&
+       echo "| |/  " >> expected &&
+       echo "|/|   " >> expected &&
+       echo "* | $A2" >> expected &&
+       echo "|/  " >> expected &&
+       echo "* $A1" >> expected &&
+       git rev-list --graph --all > actual &&
+       test_cmp expected actual
+       '
+
+# Make sure the graph_is_interesting() code still realizes
+# that undecorated merges are interesting, even with --simplify-by-decoration
+test_expect_success '--graph --simplify-by-decoration' '
+       rm -f expected &&
+       git tag -d A4
+       echo "* $A7" >> expected &&
+       echo "*   $A6" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "| * $C3" >> expected &&
+       echo "* | $A5" >> expected &&
+       echo "| |     " >> expected &&
+       echo "|  \\    " >> expected &&
+       echo "*-. \\   $A4" >> expected &&
+       echo "|\\ \\ \\  " >> expected &&
+       echo "| | |/  " >> expected &&
+       echo "| | * $C2" >> expected &&
+       echo "| | * $C1" >> expected &&
+       echo "| * | $B2" >> expected &&
+       echo "| * | $B1" >> expected &&
+       echo "* | | $A3" >> expected &&
+       echo "| |/  " >> expected &&
+       echo "|/|   " >> expected &&
+       echo "* | $A2" >> expected &&
+       echo "|/  " >> expected &&
+       echo "* $A1" >> expected &&
+       git rev-list --graph --all --simplify-by-decoration > actual &&
+       test_cmp expected actual
+       '
+
+# Get rid of all decorations on branch B, and graph with it simplified away
+test_expect_success '--graph --simplify-by-decoration prune branch B' '
+       rm -f expected &&
+       git tag -d B2
+       git tag -d B1
+       git branch -d B
+       echo "* $A7" >> expected &&
+       echo "*   $A6" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "| * $C3" >> expected &&
+       echo "* | $A5" >> expected &&
+       echo "* |   $A4" >> expected &&
+       echo "|\\ \\  " >> expected &&
+       echo "| |/  " >> expected &&
+       echo "| * $C2" >> expected &&
+       echo "| * $C1" >> expected &&
+       echo "* | $A3" >> expected &&
+       echo "|/  " >> expected &&
+       echo "* $A2" >> expected &&
+       echo "* $A1" >> expected &&
+       git rev-list --graph --simplify-by-decoration --all > actual &&
+       test_cmp expected actual
+       '
+
+test_expect_success '--graph --full-history -- bar.txt' '
+       rm -f expected &&
+       git tag -d B2
+       git tag -d B1
+       git branch -d B
+       echo "* $A7" >> expected &&
+       echo "*   $A6" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "* | $A5" >> expected &&
+       echo "* |   $A4" >> expected &&
+       echo "|\\ \\  " >> expected &&
+       echo "| |/  " >> expected &&
+       echo "* | $A3" >> expected &&
+       echo "|/  " >> expected &&
+       echo "* $A2" >> expected &&
+       git rev-list --graph --full-history --all -- bar.txt > actual &&
+       test_cmp expected actual
+       '
+
+test_expect_success '--graph --full-history --simplify-merges -- bar.txt' '
+       rm -f expected &&
+       git tag -d B2
+       git tag -d B1
+       git branch -d B
+       echo "* $A7" >> expected &&
+       echo "*   $A6" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "* | $A5" >> expected &&
+       echo "* | $A3" >> expected &&
+       echo "|/  " >> expected &&
+       echo "* $A2" >> expected &&
+       git rev-list --graph --full-history --simplify-merges --all \
+               -- bar.txt > actual &&
+       test_cmp expected actual
+       '
+
+test_expect_success '--graph -- bar.txt' '
+       rm -f expected &&
+       git tag -d B2
+       git tag -d B1
+       git branch -d B
+       echo "* $A7" >> expected &&
+       echo "* $A5" >> expected &&
+       echo "* $A3" >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "|/  " >> expected &&
+       echo "* $A2" >> expected &&
+       git rev-list --graph --all -- bar.txt > actual &&
+       test_cmp expected actual
+       '
+
+test_expect_success '--graph --sparse -- bar.txt' '
+       rm -f expected &&
+       git tag -d B2
+       git tag -d B1
+       git branch -d B
+       echo "* $A7" >> expected &&
+       echo "* $A6" >> expected &&
+       echo "* $A5" >> expected &&
+       echo "* $A4" >> expected &&
+       echo "* $A3" >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "| * $C3" >> expected &&
+       echo "| * $C2" >> expected &&
+       echo "| * $C1" >> expected &&
+       echo "|/  " >> expected &&
+       echo "* $A2" >> expected &&
+       echo "* $A1" >> expected &&
+       git rev-list --graph --sparse --all -- bar.txt > actual &&
+       test_cmp expected actual
+       '
+
+test_expect_success '--graph ^C4' '
+       rm -f expected &&
+       echo "* $A7" >> expected &&
+       echo "* $A6" >> expected &&
+       echo "* $A5" >> expected &&
+       echo "*   $A4" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $B2" >> expected &&
+       echo "| * $B1" >> expected &&
+       echo "* $A3" >> expected &&
+       git rev-list --graph --all ^C4 > actual &&
+       test_cmp expected actual
+       '
+
+test_expect_success '--graph ^C3' '
+       rm -f expected &&
+       echo "* $A7" >> expected &&
+       echo "*   $A6" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "* $A5" >> expected &&
+       echo "*   $A4" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $B2" >> expected &&
+       echo "| * $B1" >> expected &&
+       echo "* $A3" >> expected &&
+       git rev-list --graph --all ^C3 > actual &&
+       test_cmp expected actual
+       '
+
+# I don't think the ordering of the boundary commits is really
+# that important, but this test depends on it.  If the ordering ever changes
+# in the code, we'll need to update this test.
+test_expect_success '--graph --boundary ^C3' '
+       rm -f expected &&
+       echo "* $A7" >> expected &&
+       echo "*   $A6" >> expected &&
+       echo "|\\  " >> expected &&
+       echo "| * $C4" >> expected &&
+       echo "* | $A5" >> expected &&
+       echo "| |     " >> expected &&
+       echo "|  \\    " >> expected &&
+       echo "*-. \\   $A4" >> expected &&
+       echo "|\\ \\ \\  " >> expected &&
+       echo "| * | | $B2" >> expected &&
+       echo "| * | | $B1" >> expected &&
+       echo "* | | | $A3" >> expected &&
+       echo "o | | | $A2" >> expected &&
+       echo "|/ / /  " >> expected &&
+       echo "o | | $A1" >> expected &&
+       echo " / /  " >> expected &&
+       echo "| o $C3" >> expected &&
+       echo "|/  " >> expected &&
+       echo "o $C2" >> expected &&
+       git rev-list --graph --boundary --all ^C3 > actual &&
+       test_cmp expected actual
+       '
+
+test_done
index a19d49de28c457d1a5726400d25c1f731506a05f..e71c687f2b9473842684fae1a1b4b013c721497f 100755 (executable)
@@ -22,4 +22,27 @@ git commit -m "File: dir"'
 
 test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
 
+test_expect_failure 'F/D conflict' '
+       git reset --hard &&
+       git checkout master &&
+       rm .git/index &&
+
+       mkdir before &&
+       echo FILE >before/one &&
+       echo FILE >after &&
+       git add . &&
+       git commit -m first &&
+
+       rm -f after &&
+       git mv before after &&
+       git commit -m move &&
+
+       git checkout -b para HEAD^ &&
+       echo COMPLETELY ANOTHER FILE >another &&
+       git add . &&
+       git commit -m para &&
+
+       git merge master
+'
+
 test_done
index f8942bc8908fb92b1b11580e3554a81377dc3ba1..7dcf39191476f272431e19e10ebb299d6aa55bb1 100755 (executable)
@@ -54,6 +54,12 @@ deduxit me super semitas jusitiae,
 EOF
 printf "propter nomen suum." >> new4.txt
 
+test_expect_success 'merge with no changes' '
+       cp orig.txt test.txt &&
+       git merge-file test.txt orig.txt orig.txt &&
+       test_cmp test.txt orig.txt
+'
+
 cp new1.txt test.txt
 test_expect_success "merge without conflict" \
        "git merge-file test.txt orig.txt new2.txt"
index 129fa3000c9543804b43e74e27eec523e328bb5c..b3fbf659c003acbed785558c21950046b8caced8 100755 (executable)
@@ -65,18 +65,18 @@ test_expect_success "combined merge conflicts" "
 "
 
 cat > expect << EOF
-<<<<<<< HEAD:a1
+<<<<<<< HEAD
 F
 =======
 G
->>>>>>> G:a1
+>>>>>>> G
 EOF
 
 test_expect_success "result contains a conflict" "test_cmp expect a1"
 
 git ls-files --stage > out
 cat > expect << EOF
-100644 da056ce14a2241509897fa68bb2b3b6e6194ef9e 1      a1
+100644 439cc46de773d8a83c77799b7cc9191c128bfcff 1      a1
 100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2      a1
 100644 fd7923529855d0b274795ae3349c5e0438333979 3      a1
 EOF
@@ -93,8 +93,7 @@ test_expect_success 'refuse to merge binary files' '
        git add binary-file &&
        git commit -m binary2 &&
        test_must_fail git merge F > merge.out 2> merge.err &&
-       grep "Cannot merge binary files: HEAD:binary-file vs. F:binary-file" \
-               merge.err
+       grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge.err
 '
 
 test_expect_success 'mark rename/delete as unmerged' '
index 54b7ea6505d8c189c6c557cffd3d6518df06fb73..def397c53a76dead449710eaca6333c2e1fb36aa 100755 (executable)
@@ -175,7 +175,7 @@ test_expect_success 'bisect skip: successfull result' '
        git bisect start $HASH4 $HASH1 &&
        git bisect skip &&
        git bisect bad > my_bisect_log.txt &&
-       grep "$HASH2 is first bad commit" my_bisect_log.txt &&
+       grep "$HASH2 is the first bad commit" my_bisect_log.txt &&
        git bisect reset
 '
 
@@ -261,7 +261,7 @@ test_expect_success \
      git bisect good $HASH1 &&
      git bisect bad $HASH4 &&
      git bisect run ./test_script.sh > my_bisect_log.txt &&
-     grep "$HASH3 is first bad commit" my_bisect_log.txt &&
+     grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
      git bisect reset'
 
 # We want to automatically find the commit that
@@ -274,7 +274,7 @@ test_expect_success \
      chmod +x test_script.sh &&
      git bisect start $HASH4 $HASH1 &&
      git bisect run ./test_script.sh > my_bisect_log.txt &&
-     grep "$HASH4 is first bad commit" my_bisect_log.txt &&
+     grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
      git bisect reset'
 
 # $HASH1 is good, $HASH5 is bad, we skip $HASH3
@@ -287,14 +287,14 @@ test_expect_success 'bisect skip: add line and then a new test' '
        git bisect start $HASH5 $HASH1 &&
        git bisect skip &&
        git bisect good > my_bisect_log.txt &&
-       grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+       grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
        git bisect log > log_to_replay.txt &&
        git bisect reset
 '
 
 test_expect_success 'bisect skip and bisect replay' '
        git bisect replay log_to_replay.txt > my_bisect_log.txt &&
-       grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+       grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
        git bisect reset
 '
 
@@ -335,7 +335,7 @@ test_expect_success 'bisect run & skip: find first bad' '
        chmod +x test_script.sh &&
        git bisect start $HASH7 $HASH1 &&
        git bisect run ./test_script.sh > my_bisect_log.txt &&
-       grep "$HASH6 is first bad commit" my_bisect_log.txt
+       grep "$HASH6 is the first bad commit" my_bisect_log.txt
 '
 
 test_expect_success 'bisect skip only one range' '
@@ -385,7 +385,7 @@ test_expect_success 'bisect does not create a "bisect" branch' '
        rev_hash6=$(git rev-parse --verify HEAD) &&
        test "$rev_hash6" = "$HASH6" &&
        git bisect good > my_bisect_log.txt &&
-       grep "$HASH7 is first bad commit" my_bisect_log.txt &&
+       grep "$HASH7 is the first bad commit" my_bisect_log.txt &&
        git bisect reset &&
        rev_hash6=$(git rev-parse --verify bisect) &&
        test "$rev_hash6" = "$HASH6" &&
@@ -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
@@ -545,7 +534,7 @@ test_expect_success 'restricting bisection on one dir' '
        para1=$(git rev-parse --verify HEAD) &&
        test "$para1" = "$PARA_HASH1" &&
        git bisect bad > my_bisect_log.txt &&
-       grep "$PARA_HASH1 is first bad commit" my_bisect_log.txt
+       grep "$PARA_HASH1 is the first bad commit" my_bisect_log.txt
 '
 
 test_expect_success 'restricting bisection on one dir and a file' '
@@ -563,7 +552,19 @@ test_expect_success 'restricting bisection on one dir and a file' '
        para1=$(git rev-parse --verify HEAD) &&
        test "$para1" = "$PARA_HASH1" &&
        git bisect good > my_bisect_log.txt &&
-       grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
+       grep "$PARA_HASH4 is the 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 &&
+       para3=$(git rev-parse --verify HEAD) &&
+       test "$para3" = "$PARA_HASH3"
 '
 
 #
diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh
new file mode 100755 (executable)
index 0000000..5b96fb0
--- /dev/null
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='merging when a directory was replaced with a symlink'
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+       say 'Symbolic links not supported, skipping tests.'
+       test_done
+fi
+
+test_expect_success 'create a commit where dir a/b changed to symlink' '
+       mkdir -p a/b/c a/b-2/c &&
+       > a/b/c/d &&
+       > a/b-2/c/d &&
+       > a/x &&
+       git add -A &&
+       git commit -m base &&
+       git tag start &&
+       rm -rf a/b &&
+       ln -s b-2 a/b &&
+       git add -A &&
+       git commit -m "dir to symlink"
+'
+
+test_expect_success 'keep a/b-2/c/d across checkout' '
+       git checkout HEAD^0 &&
+       git reset --hard master &&
+       git rm --cached a/b &&
+       git commit -m "untracked symlink remains" &&
+        git checkout start^0 &&
+        test -f a/b-2/c/d
+'
+
+test_expect_success 'checkout should not have deleted a/b-2/c/d' '
+       git checkout HEAD^0 &&
+       git reset --hard master &&
+        git checkout start^0 &&
+        test -f a/b-2/c/d
+'
+
+test_expect_success 'setup for merge test' '
+       git reset --hard &&
+       test -f a/b-2/c/d &&
+       echo x > a/x &&
+       git add a/x &&
+       git commit -m x &&
+       git tag baseline
+'
+
+test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       git merge -s resolve master &&
+       test -h a/b &&
+       test -f a/b-2/c/d
+'
+
+test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       git merge -s recursive master &&
+       test -h a/b &&
+       test -f a/b-2/c/d
+'
+
+test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
+       git reset --hard &&
+       git checkout start^0 &&
+       rm -rf a/b-2 &&
+       ln -s b a/b-2 &&
+       git add -A &&
+       git commit -m "dir a/b-2 to symlink" &&
+       git tag test2
+'
+
+test_expect_failure 'merge should not have conflicts (resolve)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       git merge -s resolve test2 &&
+       test -h a/b-2 &&
+       test -f a/b/c/d
+'
+
+test_expect_failure 'merge should not have conflicts (recursive)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       git merge -s recursive test2 &&
+       test -h a/b-2 &&
+       test -f a/b/c/d
+'
+
+test_done
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
new file mode 100755 (executable)
index 0000000..b874141
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='recursive merge corner cases'
+
+. ./test-lib.sh
+
+#
+#  L1  L2
+#   o---o
+#  / \ / \
+# o   X   ?
+#  \ / \ /
+#   o---o
+#  R1  R2
+#
+
+test_expect_success setup '
+       ten="0 1 2 3 4 5 6 7 8 9"
+       for i in $ten
+       do
+               echo line $i in a sample file
+       done >one &&
+       for i in $ten
+       do
+               echo line $i in another sample file
+       done >two &&
+       git add one two &&
+       test_tick && git commit -m initial &&
+
+       git branch L1 &&
+       git checkout -b R1 &&
+       git mv one three &&
+       test_tick && git commit -m R1 &&
+
+       git checkout L1 &&
+       git mv two three &&
+       test_tick && git commit -m L1 &&
+
+       git checkout L1^0 &&
+       test_tick && git merge -s ours R1 &&
+       git tag L2 &&
+
+       git checkout R1^0 &&
+       test_tick && git merge -s ours L1 &&
+       git tag R2
+'
+
+test_expect_success merge '
+       git reset --hard &&
+       git checkout L2^0 &&
+
+       test_must_fail git merge -s recursive R2^0
+'
+
+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/t6050-replace.sh b/t/t6050-replace.sh
new file mode 100755 (executable)
index 0000000..8b8bd81
--- /dev/null
@@ -0,0 +1,200 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Christian Couder
+#
+test_description='Tests replace refs functionality'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_and_commit_file()
+{
+    _file="$1"
+    _msg="$2"
+
+    git add $_file || return $?
+    test_tick || return $?
+    git commit --quiet -m "$_file: $_msg"
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+HASH5=
+HASH6=
+HASH7=
+
+test_expect_success 'set up buggy branch' '
+     echo "line 1" >> hello &&
+     echo "line 2" >> hello &&
+     echo "line 3" >> hello &&
+     echo "line 4" >> hello &&
+     add_and_commit_file hello "4 lines" &&
+     HASH1=$(git rev-parse --verify HEAD) &&
+     echo "line BUG" >> hello &&
+     echo "line 6" >> hello &&
+     echo "line 7" >> hello &&
+     echo "line 8" >> hello &&
+     add_and_commit_file hello "4 more lines with a BUG" &&
+     HASH2=$(git rev-parse --verify HEAD) &&
+     echo "line 9" >> hello &&
+     echo "line 10" >> hello &&
+     add_and_commit_file hello "2 more lines" &&
+     HASH3=$(git rev-parse --verify HEAD) &&
+     echo "line 11" >> hello &&
+     add_and_commit_file hello "1 more line" &&
+     HASH4=$(git rev-parse --verify HEAD) &&
+     sed -e "s/BUG/5/" hello > hello.new &&
+     mv hello.new hello &&
+     add_and_commit_file hello "BUG fixed" &&
+     HASH5=$(git rev-parse --verify HEAD) &&
+     echo "line 12" >> hello &&
+     echo "line 13" >> hello &&
+     add_and_commit_file hello "2 more lines" &&
+     HASH6=$(git rev-parse --verify HEAD)
+     echo "line 14" >> hello &&
+     echo "line 15" >> hello &&
+     echo "line 16" >> hello &&
+     add_and_commit_file hello "again 3 more lines" &&
+     HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'replace the author' '
+     git cat-file commit $HASH2 | grep "author A U Thor" &&
+     R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+     git cat-file commit $R | grep "author O Thor" &&
+     git update-ref refs/replace/$HASH2 $R &&
+     git show HEAD~5 | grep "O Thor" &&
+     git show $HASH2 | grep "O Thor"
+'
+
+cat >tag.sig <<EOF
+object $HASH2
+type commit
+tag mytag
+tagger T A Gger <> 0 +0000
+
+EOF
+
+test_expect_success 'tag replaced commit' '
+     git mktag <tag.sig >.git/refs/tags/mytag 2>message
+'
+
+test_expect_success '"git fsck" works' '
+     git fsck master > fsck_master.out &&
+     grep "dangling commit $R" fsck_master.out &&
+     grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
+     test -z "$(git fsck)"
+'
+
+test_expect_success 'repack, clone and fetch work' '
+     git repack -a -d &&
+     git clone --no-hardlinks . clone_dir &&
+     cd clone_dir &&
+     git show HEAD~5 | grep "A U Thor" &&
+     git show $HASH2 | grep "A U Thor" &&
+     git cat-file commit $R &&
+     git repack -a -d &&
+     test_must_fail git cat-file commit $R &&
+     git fetch ../ "refs/replace/*:refs/replace/*" &&
+     git show HEAD~5 | grep "O Thor" &&
+     git show $HASH2 | grep "O Thor" &&
+     git cat-file commit $R &&
+     cd ..
+'
+
+test_expect_success '"git replace" listing and deleting' '
+     test "$HASH2" = "$(git replace -l)" &&
+     test "$HASH2" = "$(git replace)" &&
+     aa=${HASH2%??????????????????????????????????????} &&
+     test "$HASH2" = "$(git replace -l "$aa*")" &&
+     test_must_fail git replace -d $R &&
+     test_must_fail git replace -d &&
+     test_must_fail git replace -l -d $HASH2 &&
+     git replace -d $HASH2 &&
+     git show $HASH2 | grep "A U Thor" &&
+     test -z "$(git replace -l)"
+'
+
+test_expect_success '"git replace" replacing' '
+     git replace $HASH2 $R &&
+     git show $HASH2 | grep "O Thor" &&
+     test_must_fail git replace $HASH2 $R &&
+     git replace -f $HASH2 $R &&
+     test_must_fail git replace -f &&
+     test "$HASH2" = "$(git replace)"
+'
+
+# This creates a side branch where the bug in H2
+# does not appear because P2 is created by applying
+# H2 and squashing H5 into it.
+# P3, P4 and P6 are created by cherry-picking H3, H4
+# and H6 respectively.
+#
+# At this point, we should have the following:
+#
+#    P2--P3--P4--P6
+#   /
+# H1-H2-H3-H4-H5-H6-H7
+#
+# Then we replace H6 with P6.
+#
+test_expect_success 'create parallel branch without the bug' '
+     git replace -d $HASH2 &&
+     git show $HASH2 | grep "A U Thor" &&
+     git checkout $HASH1 &&
+     git cherry-pick $HASH2 &&
+     git show $HASH5 | git apply &&
+     git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
+     PARA2=$(git rev-parse --verify HEAD) &&
+     git cherry-pick $HASH3 &&
+     PARA3=$(git rev-parse --verify HEAD) &&
+     git cherry-pick $HASH4 &&
+     PARA4=$(git rev-parse --verify HEAD) &&
+     git cherry-pick $HASH6 &&
+     PARA6=$(git rev-parse --verify HEAD) &&
+     git replace $HASH6 $PARA6 &&
+     git checkout master &&
+     cur=$(git rev-parse --verify HEAD) &&
+     test "$cur" = "$HASH7" &&
+     git log --pretty=oneline | grep $PARA2 &&
+     git remote add cloned ./clone_dir
+'
+
+test_expect_success 'push to cloned repo' '
+     git push cloned $HASH6^:refs/heads/parallel &&
+     cd clone_dir &&
+     git checkout parallel &&
+     git log --pretty=oneline | grep $PARA2 &&
+     cd ..
+'
+
+test_expect_success 'push branch with replacement' '
+     git cat-file commit $PARA3 | grep "author A U Thor" &&
+     S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+     git cat-file commit $S | grep "author O Thor" &&
+     git replace $PARA3 $S &&
+     git show $HASH6~2 | grep "O Thor" &&
+     git show $PARA3 | grep "O Thor" &&
+     git push cloned $HASH6^:refs/heads/parallel2 &&
+     cd clone_dir &&
+     git checkout parallel2 &&
+     git log --pretty=oneline | grep $PARA3 &&
+     git show $PARA3 | grep "A U Thor" &&
+     cd ..
+'
+
+test_expect_success 'fetch branch with replacement' '
+     git branch tofetch $HASH6 &&
+     cd clone_dir &&
+     git fetch origin refs/heads/tofetch:refs/heads/parallel3
+     git log --pretty=oneline parallel3 | grep $PARA3
+     git show $PARA3 | grep "A U Thor"
+     cd ..
+'
+
+#
+#
+test_done
index 2049ab6cf844da233837b58857cfde8a2d563252..42f6fff373ba9707216279011b112c6c59af8780 100755 (executable)
@@ -208,4 +208,36 @@ test_expect_success 'merge-msg test #5-2' '
        test_cmp expected actual
 '
 
+test_expect_success 'merge-msg -F' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.summary yes &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left right &&
+
+       git fmt-merge-msg -F .git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'merge-msg -F in subdirectory' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.summary yes &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left right &&
+       mkdir sub &&
+       cp .git/FETCH_HEAD sub/FETCH_HEAD &&
+       (
+               cd sub &&
+               git fmt-merge-msg -F FETCH_HEAD >../actual
+       ) &&
+       test_cmp expected actual
+'
+
 test_done
index b81593780a2a6adaf34bb10293b607e3a303cfcd..ae5290ab43e8b51b32331768371e2a67d700064d 100755 (executable)
@@ -8,6 +8,15 @@ test_description='git grep various.
 
 . ./test-lib.sh
 
+cat >hello.c <<EOF
+#include <stdio.h>
+int main(int argc, const char **argv)
+{
+       printf("Hello world.\n");
+       return 0;
+}
+EOF
+
 test_expect_success setup '
        {
                echo foo mmap bar
@@ -16,12 +25,17 @@ test_expect_success setup '
                echo foo mmap bar_mmap
                echo foo_mmap bar mmap baz
        } >file &&
+       echo vvv >v &&
+       echo ww w >w &&
        echo x x xx x >x &&
        echo y yy >y &&
        echo zzz > z &&
        mkdir t &&
        echo test >t/t &&
-       git add file x y z t/t &&
+       echo vvv >t/v &&
+       mkdir t/a &&
+       echo vvv >t/a/v &&
+       git add . &&
        test_tick &&
        git commit -m initial
 '
@@ -48,6 +62,12 @@ do
                diff expected actual
        '
 
+       test_expect_success "grep -w $L (w)" '
+               : >expected &&
+               ! git grep -n -w -e "^w" >actual &&
+               test_cmp expected actual
+       '
+
        test_expect_success "grep -w $L (x)" '
                {
                        echo ${HC}x:1:x x xx x
@@ -116,8 +136,171 @@ do
                ! git grep -c test $H | grep /dev/null
         '
 
+       test_expect_success "grep --max-depth -1 $L" '
+               {
+                       echo ${HC}t/a/v:1:vvv
+                       echo ${HC}t/v:1:vvv
+                       echo ${HC}v:1:vvv
+               } >expected &&
+               git grep --max-depth -1 -n -e vvv $H >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep --max-depth 0 $L" '
+               {
+                       echo ${HC}v:1:vvv
+               } >expected &&
+               git grep --max-depth 0 -n -e vvv $H >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep --max-depth 0 -- '*' $L" '
+               {
+                       echo ${HC}t/a/v:1:vvv
+                       echo ${HC}t/v:1:vvv
+                       echo ${HC}v:1:vvv
+               } >expected &&
+               git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep --max-depth 1 $L" '
+               {
+                       echo ${HC}t/v:1:vvv
+                       echo ${HC}v:1:vvv
+               } >expected &&
+               git grep --max-depth 1 -n -e vvv $H >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep --max-depth 0 -- t $L" '
+               {
+                       echo ${HC}t/v:1:vvv
+               } >expected &&
+               git grep --max-depth 0 -n -e vvv $H -- t >actual &&
+               test_cmp expected actual
+       '
+
 done
 
+cat >expected <<EOF
+file:foo mmap bar_mmap
+EOF
+
+test_expect_success 'grep -e A --and -e B' '
+       git grep -e "foo mmap" --and -e bar_mmap >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo_mmap bar mmap
+file:foo_mmap bar mmap baz
+EOF
+
+
+test_expect_success 'grep ( -e A --or -e B ) --and -e B' '
+       git grep \( -e foo_ --or -e baz \) \
+               --and -e " mmap" >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+EOF
+
+test_expect_success 'grep -e A --and --not -e B' '
+       git grep -e "foo mmap" --and --not -e bar_mmap >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -f, non-existent file' '
+       test_must_fail git grep -f patterns
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+EOF
+
+cat >pattern <<EOF
+mmap
+EOF
+
+test_expect_success 'grep -f, one pattern' '
+       git grep -f pattern >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+t/a/v:vvv
+t/v:vvv
+v:vvv
+EOF
+
+cat >patterns <<EOF
+mmap
+vvv
+EOF
+
+test_expect_success 'grep -f, multiple patterns' '
+       git grep -f patterns >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+t/a/v:vvv
+t/v:vvv
+v:vvv
+EOF
+
+cat >patterns <<EOF
+
+mmap
+
+vvv
+
+EOF
+
+test_expect_success 'grep -f, ignore empty lines' '
+       git grep -f patterns >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+y:y yy
+--
+z:zzz
+EOF
+
+# Create 1024 file names that sort between "y" and "z" to make sure
+# the two files are handled by different calls to an external grep.
+# This depends on MAXARGS in builtin-grep.c being 1024 or less.
+c32="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v"
+test_expect_success 'grep -C1, hunk mark between files' '
+       for a in $c32; do for b in $c32; do : >y-$a$b; done; done &&
+       git add y-?? &&
+       git grep -C1 "^[yz]" >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -C1 --no-ext-grep, hunk mark between files' '
+       git grep -C1 --no-ext-grep "^[yz]" >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'log grep setup' '
        echo a >>file &&
        test_tick &&
@@ -170,9 +353,62 @@ test_expect_success 'log grep (6)' '
 test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&
-       test "$(git grep --no-ext-grep t)" = "t/t:test" &&
+       test "$(git grep --no-ext-grep test)" = "t/t:test" &&
        git update-index --no-assume-unchanged t/t &&
        git checkout t/t
 '
 
+cat >expected <<EOF
+hello.c=#include <stdio.h>
+hello.c:       return 0;
+EOF
+
+test_expect_success 'grep -p with userdiff' '
+       git config diff.custom.funcname "^#" &&
+       echo "hello.c diff=custom" >.gitattributes &&
+       git grep -p return >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c:       return 0;
+EOF
+
+test_expect_success 'grep -p' '
+       rm -f .gitattributes &&
+       git grep -p return >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c-#include <stdio.h>
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c-       printf("Hello world.\n");
+hello.c:       return 0;
+EOF
+
+test_expect_success 'grep -p -B5' '
+       git grep -p -B5 return >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep from a subdirectory to search wider area (1)' '
+       mkdir -p s &&
+       (
+               cd s && git grep "x x x" ..
+       )
+'
+
+test_expect_success 'grep from a subdirectory to search wider area (2)' '
+       mkdir -p s &&
+       (
+               cd s || exit 1
+               ( git grep xxyyzz .. >out ; echo $? >status )
+               ! test -s out &&
+               test 1 = $(cat status)
+       )
+'
+
 test_done
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
new file mode 100755 (executable)
index 0000000..1044aa6
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='basic work tree status reporting'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_commit A &&
+       test_commit B oneside added &&
+       git checkout A^0 &&
+       test_commit C oneside created
+'
+
+test_expect_success 'A/A conflict' '
+       git checkout B^0 &&
+       test_must_fail git merge C
+'
+
+test_expect_success 'Report path with conflict' '
+       git diff --cached --name-status >actual &&
+       echo "U oneside" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Report new path with conflict' '
+       git diff --cached --name-status HEAD^ >actual &&
+       echo "U oneside" >expect &&
+       test_cmp expect actual
+'
+
+cat >expect <<EOF
+# On branch side
+# Unmerged paths:
+#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git add <file>..." to mark resolution)
+#
+#      deleted by us:      foo
+#
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success 'M/D conflict does not segfault' '
+       mkdir mdconflict &&
+       (
+               cd mdconflict &&
+               git init &&
+               test_commit initial foo "" &&
+               test_commit modify foo foo &&
+               git checkout -b side HEAD^ &&
+               git rm foo &&
+               git commit -m delete &&
+               test_must_fail git merge master &&
+               test_must_fail git status > ../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_done
index e637c7d4dbdce337b4d005719c4aa86d447a70d9..e85ff02c3e636567a80586e92fdba1c5382fc995 100755 (executable)
@@ -419,7 +419,8 @@ test_expect_success 'resetting an unmodified path is a no-op' '
 '
 
 cat > expect << EOF
-file2: locally modified
+Unstaged changes after reset:
+M      file2
 EOF
 
 test_expect_success '--mixed refreshes the index' '
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
new file mode 100755 (executable)
index 0000000..c1f4fc3
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git reset --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+       mkdir dir &&
+       echo parent > dir/foo &&
+       echo dummy > bar &&
+       git add dir &&
+       git commit -m initial &&
+       test_tick &&
+       test_commit second dir/foo head &&
+       set_and_save_state bar bar_work bar_index &&
+       save_head
+'
+
+# note: bar sorts before foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+       set_and_save_state dir/foo work work
+       (echo n; echo n) | git reset -p &&
+       verify_saved_state dir/foo &&
+       verify_saved_state bar
+'
+
+test_expect_success 'git reset -p' '
+       (echo n; echo y) | git reset -p &&
+       verify_state dir/foo work head &&
+       verify_saved_state bar
+'
+
+test_expect_success 'git reset -p HEAD^' '
+       (echo n; echo y) | git reset -p HEAD^ &&
+       verify_state dir/foo work parent &&
+       verify_saved_state bar
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success 'git reset -p dir' '
+       set_state dir/foo work work
+       (echo y; echo n) | git reset -p dir &&
+       verify_state dir/foo work head &&
+       verify_saved_state bar
+'
+
+test_expect_success 'git reset -p -- foo (inside dir)' '
+       set_state dir/foo work work
+       (echo y; echo n) | (cd dir && git reset -p -- foo) &&
+       verify_state dir/foo work head &&
+       verify_saved_state bar
+'
+
+test_expect_success 'git reset -p HEAD^ -- dir' '
+       (echo y; echo n) | git reset -p HEAD^ -- dir &&
+       verify_state dir/foo work parent &&
+       verify_saved_state bar
+'
+
+test_expect_success 'none of this moved HEAD' '
+       verify_saved_head
+'
+
+
+test_done
index bdb808af1aefae97c297f7845a8cd3b595404e89..ebfd34df36068f8808406a98d371731fb85012c4 100755 (executable)
@@ -534,4 +534,12 @@ test_expect_success 'failing checkout -b should not break working tree' '
 
 '
 
+test_expect_success 'switch out of non-branch' '
+       git reset --hard master &&
+       git checkout master^0 &&
+       echo modified >one &&
+       test_must_fail git checkout renamer 2>error.log &&
+       ! grep "^Previous HEAD" error.log
+'
+
 test_done
index 929d5d4d3b9d55f570cef1617a0716b17265c988..118c6ebb182b5cd4700e533d6a951b31529149af 100755 (executable)
@@ -380,4 +380,43 @@ test_expect_success 'removal failure' '
 '
 chmod 755 foo
 
+test_expect_success 'nested git work tree' '
+       rm -fr foo bar &&
+       mkdir foo bar &&
+       (
+               cd foo &&
+               git init &&
+               >hello.world
+               git add . &&
+               git commit -a -m nested
+       ) &&
+       (
+               cd bar &&
+               >goodbye.people
+       ) &&
+       git clean -f -d &&
+       test -f foo/.git/index &&
+       test -f foo/hello.world &&
+       ! test -d bar
+'
+
+test_expect_success 'force removal of nested git work tree' '
+       rm -fr foo bar &&
+       mkdir foo bar &&
+       (
+               cd foo &&
+               git init &&
+               >hello.world
+               git add . &&
+               git commit -a -m nested
+       ) &&
+       (
+               cd bar &&
+               >goodbye.people
+       ) &&
+       git clean -f -f -d &&
+       ! test -d foo &&
+       ! test -d bar
+'
+
 test_done
index 61498293b99e1cbbb4bfb4de45b6d14488aaddd0..6cc16c39fe75ee9dab942fc5ae376827d3b7ea44 100755 (executable)
@@ -56,6 +56,15 @@ test_expect_success 'modified submodule(forward)' "
 EOF
 "
 
+test_expect_success 'modified submodule(forward), --files' "
+       git submodule summary --files >actual &&
+       diff actual - <<-EOF
+* sm1 $head1...$head2 (1):
+  > Add foo3
+
+EOF
+"
+
 commit_file sm1 &&
 cd sm1 &&
 git reset --hard HEAD~2 >/dev/null &&
@@ -114,6 +123,15 @@ test_expect_success 'typechanged submodule(submodule->blob), --cached' "
 EOF
 "
 
+test_expect_success 'typechanged submodule(submodule->blob), --files' "
+    git submodule summary --files >actual &&
+    diff actual - <<-EOF
+* sm1 $head5(blob)->$head4(submodule) (3):
+  > Add foo5
+
+EOF
+"
+
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
@@ -205,4 +223,8 @@ test_expect_success '--for-status' "
 EOF
 "
 
+test_expect_success 'fail when using --files together with --cached' "
+    test_must_fail git submodule summary --files --cached
+"
+
 test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
new file mode 100755 (executable)
index 0000000..2d33d9e
--- /dev/null
@@ -0,0 +1,198 @@
+#!/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/--merge" 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 --merge staying on master' '
+       (cd super/submodule &&
+         git reset --hard HEAD~1
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update --merge 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 - merge in .git/config' '
+       (cd super &&
+        git config submodule.submodule.update merge
+       ) &&
+       (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 --merge 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 --merge 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_expect_success 'submodule init picks up merge' '
+       (cd super &&
+        git config submodule.merging.url git://non-existing/git &&
+        git config submodule.merging.path does-not-matter &&
+        git config submodule.merging.update merge &&
+        git submodule init merging &&
+        test "merge" = $(git config submodule.merging.update)
+       )
+'
+
+test_done
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
new file mode 100755 (executable)
index 0000000..2a52775
--- /dev/null
@@ -0,0 +1,237 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Johan Herland
+#
+
+test_description='Test "git submodule foreach"
+
+This test verifies that "git submodule foreach" correctly visits all submodules
+that are currently checked out.
+'
+
+. ./test-lib.sh
+
+
+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 sub1 &&
+               git submodule add ../submodule sub2 &&
+               git submodule add ../submodule sub3 &&
+               git config -f .gitmodules --rename-section \
+                       submodule.sub1 submodule.foo1 &&
+               git config -f .gitmodules --rename-section \
+                       submodule.sub2 submodule.foo2 &&
+               git config -f .gitmodules --rename-section \
+                       submodule.sub3 submodule.foo3 &&
+               git add .gitmodules
+               test_tick &&
+               git commit -m "submodules" &&
+               git submodule init sub1 &&
+               git submodule init sub2 &&
+               git submodule init sub3
+       ) &&
+       (
+               cd submodule &&
+               echo different > file &&
+               git add file &&
+               test_tick &&
+               git commit -m "different"
+       ) &&
+       (
+               cd super &&
+               (
+                       cd sub3 &&
+                       git pull
+               ) &&
+               git add sub3 &&
+               test_tick &&
+               git commit -m "update sub3"
+       )
+'
+
+sub1sha1=$(cd super/sub1 && git rev-parse HEAD)
+sub3sha1=$(cd super/sub3 && git rev-parse HEAD)
+
+cat > expect <<EOF
+Entering 'sub1'
+foo1-sub1-$sub1sha1
+Entering 'sub3'
+foo3-sub3-$sub3sha1
+EOF
+
+test_expect_success 'test basic "submodule foreach" usage' '
+       git clone super clone &&
+       (
+               cd clone &&
+               git submodule update --init -- sub1 sub3 &&
+               git submodule foreach "echo \$name-\$path-\$sha1" > ../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success 'setup nested submodules' '
+       git clone submodule nested1 &&
+       git clone submodule nested2 &&
+       git clone submodule nested3 &&
+       (
+               cd nested3 &&
+               git submodule add ../submodule submodule &&
+               test_tick &&
+               git commit -m "submodule" &&
+               git submodule init submodule
+       ) &&
+       (
+               cd nested2 &&
+               git submodule add ../nested3 nested3 &&
+               test_tick &&
+               git commit -m "nested3" &&
+               git submodule init nested3
+       ) &&
+       (
+               cd nested1 &&
+               git submodule add ../nested2 nested2 &&
+               test_tick &&
+               git commit -m "nested2" &&
+               git submodule init nested2
+       ) &&
+       (
+               cd super &&
+               git submodule add ../nested1 nested1 &&
+               test_tick &&
+               git commit -m "nested1" &&
+               git submodule init nested1
+       )
+'
+
+test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
+       git clone super clone2 &&
+       (
+               cd clone2 &&
+               test ! -d sub1/.git &&
+               test ! -d sub2/.git &&
+               test ! -d sub3/.git &&
+               test ! -d nested1/.git &&
+               git submodule update --init &&
+               test -d sub1/.git &&
+               test -d sub2/.git &&
+               test -d sub3/.git &&
+               test -d nested1/.git &&
+               test ! -d nested1/nested2/.git &&
+               git submodule foreach "git submodule update --init" &&
+               test -d nested1/nested2/.git &&
+               test ! -d nested1/nested2/nested3/.git
+       )
+'
+
+test_expect_success 'use "foreach --recursive" to checkout all submodules' '
+       (
+               cd clone2 &&
+               git submodule foreach --recursive "git submodule update --init" &&
+               test -d nested1/nested2/nested3/.git &&
+               test -d nested1/nested2/nested3/submodule/.git
+       )
+'
+
+cat > expect <<EOF
+Entering 'nested1'
+Entering 'nested1/nested2'
+Entering 'nested1/nested2/nested3'
+Entering 'nested1/nested2/nested3/submodule'
+Entering 'sub1'
+Entering 'sub2'
+Entering 'sub3'
+EOF
+
+test_expect_success 'test messages from "foreach --recursive"' '
+       (
+               cd clone2 &&
+               git submodule foreach --recursive "true" > ../actual
+       ) &&
+       test_cmp expect actual
+'
+
+cat > expect <<EOF
+nested1-nested1
+nested2-nested2
+nested3-nested3
+submodule-submodule
+foo1-sub1
+foo2-sub2
+foo3-sub3
+EOF
+
+test_expect_success 'test "foreach --quiet --recursive"' '
+       (
+               cd clone2 &&
+               git submodule foreach -q --recursive "echo \$name-\$path" > ../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success 'use "update --recursive" to checkout all submodules' '
+       git clone super clone3 &&
+       (
+               cd clone3 &&
+               test ! -d sub1/.git &&
+               test ! -d sub2/.git &&
+               test ! -d sub3/.git &&
+               test ! -d nested1/.git &&
+               git submodule update --init --recursive &&
+               test -d sub1/.git &&
+               test -d sub2/.git &&
+               test -d sub3/.git &&
+               test -d nested1/.git &&
+               test -d nested1/nested2/.git &&
+               test -d nested1/nested2/nested3/.git &&
+               test -d nested1/nested2/nested3/submodule/.git
+       )
+'
+
+nested1sha1=$(cd clone3/nested1 && git rev-parse HEAD)
+nested2sha1=$(cd clone3/nested1/nested2 && git rev-parse HEAD)
+nested3sha1=$(cd clone3/nested1/nested2/nested3 && git rev-parse HEAD)
+submodulesha1=$(cd clone3/nested1/nested2/nested3/submodule && git rev-parse HEAD)
+sub1sha1=$(cd clone3/sub1 && git rev-parse HEAD)
+sub2sha1=$(cd clone3/sub2 && git rev-parse HEAD)
+sub3sha1=$(cd clone3/sub3 && git rev-parse HEAD)
+sub1sha1_short=$(cd clone3/sub1 && git rev-parse --short HEAD)
+sub2sha1_short=$(cd clone3/sub2 && git rev-parse --short HEAD)
+
+cat > expect <<EOF
+ $nested1sha1 nested1 (heads/master)
+ $nested2sha1 nested1/nested2 (heads/master)
+ $nested3sha1 nested1/nested2/nested3 (heads/master)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+ $sub1sha1 sub1 ($sub1sha1_short)
+ $sub2sha1 sub2 ($sub2sha1_short)
+ $sub3sha1 sub3 (heads/master)
+EOF
+
+test_expect_success 'test "status --recursive"' '
+       (
+               cd clone3 &&
+               git submodule status --recursive > ../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success 'use "git clone --recursive" to checkout all submodules' '
+       git clone --recursive super clone4 &&
+       test -d clone4/.git &&
+       test -d clone4/sub1/.git &&
+       test -d clone4/sub2/.git &&
+       test -d clone4/sub3/.git &&
+       test -d clone4/nested1/.git &&
+       test -d clone4/nested1/nested2/.git &&
+       test -d clone4/nested1/nested2/nested3/.git &&
+       test -d clone4/nested1/nested2/nested3/submodule/.git
+'
+
+test_done
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-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
index 5998baf27b5ad0fb32bf0fd42023d029a65d0e6b..8eec0fa9bc7278981b9a16571c588ede0a94d341 100755 (executable)
@@ -183,4 +183,14 @@ test_expect_success 'commit message from stdin' '
        commit_msg_is "Log with foo word"
 '
 
+test_expect_success 'commit -F overrides -t' '
+       (
+               cd subdir &&
+               echo "-F log" > f.log &&
+               echo "-t template" > t.template &&
+               git commit --allow-empty -F f.log -t t.template
+       ) &&
+       commit_msg_is "-F log"
+'
+
 test_done
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
new file mode 100755 (executable)
index 0000000..28d5679
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='test auto-generated merge messages'
+. ./test-lib.sh
+
+check_oneline() {
+       echo "$1" | sed "s/Q/'/g" >expect &&
+       git log -1 --pretty=tformat:%s >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'merge local branch' '
+       test_commit master-1 &&
+       git checkout -b local-branch &&
+       test_commit branch-1 &&
+       git checkout master &&
+       test_commit master-2 &&
+       git merge local-branch &&
+       check_oneline "Merge branch Qlocal-branchQ"
+'
+
+test_expect_success 'merge octopus branches' '
+       git checkout -b octopus-a master &&
+       test_commit octopus-1 &&
+       git checkout -b octopus-b master &&
+       test_commit octopus-2 &&
+       git checkout master &&
+       git merge octopus-a octopus-b &&
+       check_oneline "Merge branches Qoctopus-aQ and Qoctopus-bQ"
+'
+
+test_expect_success 'merge tag' '
+       git checkout -b tag-branch master &&
+       test_commit tag-1 &&
+       git checkout master &&
+       test_commit master-3 &&
+       git merge tag-1 &&
+       check_oneline "Merge commit Qtag-1Q"
+'
+
+test_expect_success 'ambiguous tag' '
+       git checkout -b ambiguous master &&
+       test_commit ambiguous &&
+       git checkout master &&
+       test_commit master-4 &&
+       git merge ambiguous &&
+       check_oneline "Merge commit QambiguousQ"
+'
+
+test_expect_success 'remote branch' '
+       git checkout -b remote master &&
+       test_commit remote-1 &&
+       git update-ref refs/remotes/origin/master remote &&
+       git checkout master &&
+       test_commit master-5 &&
+       git merge origin/master &&
+       check_oneline "Merge remote branch Qorigin/masterQ"
+'
+
+test_done
index 87c9b0e121db6c8ab7074e6c4968cd06d37851fe..f4aa0547501a19fe570304e830504ff984f5a9a9 100755 (executable)
@@ -149,5 +149,17 @@ test_expect_success 'local packed unreachable obs that exist in alternate ODB ar
        test_must_fail git show $csha1
 '
 
+test_expect_success 'objects made unreachable by grafts only are kept' '
+       test_tick &&
+       git commit --allow-empty -m "commit 4" &&
+       H0=$(git rev-parse HEAD) &&
+       H1=$(git rev-parse HEAD^) &&
+       H2=$(git rev-parse HEAD^^) &&
+       echo "$H0 $H2" > .git/info/grafts &&
+       git reflog expire --expire=now --expire-unreachable=now --all &&
+       git repack -a -d &&
+       git cat-file -t $H1
+       '
+
 test_done
 
index 966bb0a61a89ed63dec085338d3c45f766a7f777..13c25f1d528ca1ec90575e42e0393accff5d8f35 100755 (executable)
@@ -129,4 +129,19 @@ test_expect_success 'blame wholesale copy and more' '
 
 '
 
+test_expect_success 'blame path that used to be a directory' '
+       mkdir path &&
+       echo A A A A A >path/file &&
+       echo B B B B B >path/elif &&
+       git add path &&
+       test_tick &&
+       git commit -m "path was a directory" &&
+       rm -fr path &&
+       echo A A A A A >path &&
+       git add path &&
+       test_tick &&
+       git commit -m "path is a regular file" &&
+       git blame HEAD^.. -- path
+'
+
 test_done
index fcd5c266754a2b5de3dbbf94c8ef25aebff061f2..cb390559f947fe3cd89a99b9bd473d60c7083307 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/euc-japan.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 "EUC-JAPAN LINE" >> file &&
        git add file &&
-       git config i18n.commitencoding cp1251 &&
-       git commit --author "$CP1251_NAME <cp1251@localhost>" -m "$CP1251_MSG" &&
+       git config i18n.commitencoding eucJP &&
+       git commit --author "$EUC_JAPAN_NAME <euc-japan@localhost>" -m "$EUC_JAPAN_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 $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
 EOF
 
 test_expect_success \
        'blame respects i18n.logoutputencoding' '
-       git config i18n.logoutputencoding cp1251 &&
+       git config i18n.logoutputencoding eucJP &&
        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 $EUC_JAPAN_NAME
+summary $EUC_JAPAN_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/euc-japan.txt b/t/t8005/euc-japan.txt
new file mode 100644 (file)
index 0000000..288f040
--- /dev/null
@@ -0,0 +1,2 @@
+EUC_JAPAN_NAME="»³ÅÄ ÂÀϺ"
+EUC_JAPAN_MSG="¥Ö¥ì¡¼¥à¤Î¥Æ¥¹¥È¤Ç¤¹¡£"
index 2ccfbad207c6e96b1f4f528031d9e4938d364b92..bbdefeaced4b54f98e5d9a85ddd8e0d7346fe7e3 100644 (file)
@@ -1,2 +1,2 @@
-SJIS_NAME="\84I\84r\84p\84\84P\84u\84\84\84\82\84\80\84r\84y\84\89 \84R\84y\84t\84\80\84\82\84\80\84r"
-SJIS_MSG="\84S\84u\84\83\84\84\84\80\84r\84\80\84\84\83\84\80\84\80\84q\84\8b\84u\84~\84y\84u"
+SJIS_NAME="\8eR\93\91¾\98Y"
+SJIS_MSG="\83u\83\8c\81[\83\80\82Ì\83e\83X\83g\82Å\82·\81B"
index f46cfc56d80797740c3ec15e166add052f905fcb..4d00dbea7659ee27fda283e7e45cfb2d5f6ea4d1 100644 (file)
@@ -1,2 +1,2 @@
-UTF8_NAME="Иван Петрович Сидоров"
-UTF8_MSG="Тестовое сообщение"
+UTF8_NAME="山田 太郎"
+UTF8_MSG="ブレームのテストです。"
index ce26ea4ac53a4608f6f8234b67868a9cc85be7f8..fb606a9f05bd6964c588f1ba9e5422e757d12387 100755 (executable)
@@ -148,6 +148,25 @@ 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 "#!$SHELL_PATH"
+         echo sed -n -e 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 +293,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 +301,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 +335,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 +388,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 +408,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 +453,7 @@ Result: OK
 EOF
 
 test_expect_success '--suppress-cc=sob' '
+       git config --unset sendemail.cccmd
        test_suppression sob
 '
 
@@ -533,7 +606,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 +623,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 +646,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 +659,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 +694,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..570e0359e4739e178b1836887c53ddd78e3c8ec0 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'
@@ -231,6 +231,25 @@ test_expect_success \
                               "^:refs/${remotes_git_svn}$"
         '
 
+test_expect_success 'dcommit $rev does not clobber current branch' '
+       git svn fetch -i bar &&
+       git checkout -b my-bar refs/remotes/bar &&
+       echo 1 > foo &&
+       git add foo &&
+       git commit -m "change 1" &&
+       echo 2 > foo &&
+       git add foo &&
+       git commit -m "change 2" &&
+       old_head=$(git rev-parse HEAD) &&
+       git svn dcommit -i bar HEAD^ &&
+       test $old_head = $(git rev-parse HEAD) &&
+       test refs/heads/my-bar = $(git symbolic-ref HEAD) &&
+       git log refs/remotes/bar | grep "change 1" &&
+       ! git log refs/remotes/bar | grep "change 2" &&
+       git checkout master &&
+       git branch -D my-bar
+       '
+
 test_expect_success 'able to dcommit to a subdirectory' "
        git svn fetch -i bar &&
        git checkout -b my-bar refs/remotes/bar &&
index 1e31d6ea7253ee4216347fd707d1408f97af32fa..929499e996bc33d96ebab0b01ebcac8240e689b7 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,12 @@ 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
@@ -171,6 +173,7 @@ test_expect_success 'test create-ignore' "
        "
 
 cat >prop.expect <<\EOF
+
 no-such-file*
 
 EOF
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..bbfd7f4793ab41118e7060bf7c0512309f81a546 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,31 +152,31 @@ 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 &&
        git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 &&
        git update-ref -d refs/remotes/stunk &&
        git config --unset svn-remote.svn.fetch stunk &&
-       mkdir -p "$GIT_DIR"/svn/flunk@18 &&
-       rev_map=$(cd "$GIT_DIR"/svn/stunk && ls .rev_map*) &&
-       dd if="$GIT_DIR"/svn/stunk/$rev_map \
-          of="$GIT_DIR"/svn/flunk@18/$rev_map bs=24 count=1 &&
-       rm -rf "$GIT_DIR"/svn/stunk &&
+       mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 &&
+       rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) &&
+       dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \
+          of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 &&
+       rm -rf "$GIT_DIR"/svn/refs/remotes/stunk &&
        git svn init --minimize-url -i flunk "$svnrepo"/flunk &&
        git svn fetch -i flunk &&
        git svn init --minimize-url -i stunk "$svnrepo"/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..901b8e09fbc6660456311c04b4b88c20e108c313 100755 (executable)
@@ -12,13 +12,11 @@ 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 &&
-       mv "$GIT_DIR"/svn/* "$GIT_DIR"/ &&
-       mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ &&
-       rmdir "$GIT_DIR"/svn &&
+       rm -rf "$GIT_DIR"/svn &&
        git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} &&
        git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} &&
        git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn}
@@ -56,7 +54,15 @@ test_expect_success 'initialize a multi-repository repo' '
        git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" &&
        for i in tags/0.1 tags/0.2 tags/0.3; do
                git config --add svn-remote.svn.fetch \
-                                $i:refs/remotes/$i || exit 1; done
+                                $i:refs/remotes/$i || exit 1; done &&
+       git config --get-all svn-remote.svn.fetch > fetch.out &&
+       grep "^trunk:refs/remotes/trunk$" fetch.out &&
+       grep "^branches/a:refs/remotes/a$" fetch.out &&
+       grep "^branches/b:refs/remotes/b$" fetch.out &&
+       grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
+       grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
+       grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
+       grep "^:refs/${remotes_git_svn}" fetch.out
        '
 
 # refs should all be different, but the trees should all be the same:
@@ -79,36 +85,36 @@ test_expect_success 'migrate --minimize on old inited layout' '
        rm -rf "$GIT_DIR"/svn &&
        for i in `cat fetch.out`; do
                path=`expr $i : "\([^:]*\):.*$"`
-               ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"`
+               ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"`
                if test -z "$ref"; then continue; fi
                if test -n "$path"; then path="/$path"; fi
                ( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
                echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
        done &&
        git svn migrate --minimize &&
-       test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" &&
+       test -z "`git config -l | grep "^svn-remote\.git-svn\."`" &&
        git config --get-all svn-remote.svn.fetch > fetch.out &&
        grep "^trunk:refs/remotes/trunk$" fetch.out &&
        grep "^branches/a:refs/remotes/a$" fetch.out &&
        grep "^branches/b:refs/remotes/b$" fetch.out &&
        grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
        grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
-       grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out
+       grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
        grep "^:refs/${remotes_git_svn}" fetch.out
        '
 
 test_expect_success  ".rev_db auto-converted to .rev_map.UUID" '
        git svn fetch -i trunk &&
-       test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
-       expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" &&
+       test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+       expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" &&
        test -n "$expect" &&
        rev_db="$(echo $expect | sed -e "s,_map,_db,")" &&
        convert_to_rev_db "$expect" "$rev_db" &&
        rm -f "$expect" &&
        test -f "$rev_db" &&
        git svn fetch -i trunk &&
-       test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
-       test ! -e "$GIT_DIR"/svn/trunk/.rev_db &&
+       test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+       test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db &&
        test -f "$expect"
        '
 
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..9d9ebd533cbcbcfc1e23be51bad1e63f4f08c53b 100755 (executable)
@@ -9,22 +9,69 @@ 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" &&
+       svn_cmd cp -m "branch" "$svnrepo/pr ject/trunk" \
+         "$svnrepo/pr ject/branches/b" &&
+       svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
+         "$svnrepo/pr ject/tags/v1" &&
        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 ..
+'
+
+# SVN works either way, so should we...
+
+test_expect_success 'svn checkout with percent escapes' '
+       svn_cmd checkout "$svnrepo/pr%20ject" svn.percent &&
+       svn_cmd checkout "$svnrepo/pr%20ject/trunk" svn.percent.trunk
+'
+
+test_expect_success 'svn checkout with space' '
+       svn_cmd checkout "$svnrepo/pr ject" svn.space &&
+       svn_cmd checkout "$svnrepo/pr ject/trunk" svn.space.trunk
+'
+
+test_expect_success 'test clone trunk with percent escapes and minimize-url' '
+       git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize &&
+       (
+               cd minimize &&
+               git rev-parse refs/${remotes_git_svn}
+       )
+'
+
+test_expect_success 'test clone trunk with percent escapes' '
+       git svn clone "$svnrepo/pr%20ject/trunk" trunk &&
+       (
+               cd trunk &&
+               git rev-parse refs/${remotes_git_svn}
+       )
+'
+
+test_expect_success 'test clone --stdlayout with percent escapes' '
+       git svn clone --stdlayout "$svnrepo/pr%20ject" percent &&
+       (
+               cd percent &&
+               git rev-parse refs/remotes/trunk^0 &&
+               git rev-parse refs/remotes/b^0 &&
+               git rev-parse refs/remotes/tags/v1^0
+       )
+'
+
+test_expect_success 'test clone -s with unescaped space' '
+       git svn clone -s "$svnrepo/pr ject" space &&
+       (
+               cd space &&
+               git rev-parse refs/remotes/trunk^0 &&
+               git rev-parse refs/remotes/b^0 &&
+               git rev-parse refs/remotes/tags/v1^0
+       )
+'
 
 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 03705fa4ce832658f39f586d430fcbbf902bd16f..5280e5f1e405f2a93620b496c64e85e73181f13a 100755 (executable)
@@ -10,7 +10,12 @@ test_expect_success 'load svn dumpfile'  '
 test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
 
 test_expect_success 'test that b1 exists and is empty' '
-       (cd x && test -f b1 && ! test -s b1)
+       (
+               cd x &&
+               git reset --hard branch-c &&
+               test -f b1 &&
+               ! test -s b1
+       )
        '
 
 test_done
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..83cc5fc
--- /dev/null
@@ -0,0 +1,83 @@
+#!/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> "
+       )
+       '
+
+git --git-dir=x/.git config --unset svn.authorsfile
+git --git-dir=x/.git config --unset svn.authorsprog
+
+test_expect_success 'authors-prog handled special characters in username' '
+       svn mkdir -m bad --username "xyz; touch evil" "$svnrepo"/bad &&
+       (
+               cd x &&
+               git svn --authors-prog=../svn-authors-prog fetch &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn |
+               grep "^author xyz; touch evil <xyz; touch evil@example\.com> " &&
+               ! test -f evil
+       )
+'
+
+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
diff --git a/t/t9140-git-svn-reset.sh b/t/t9140-git-svn-reset.sh
new file mode 100755 (executable)
index 0000000..0735526
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Ben Jackson
+#
+
+test_description='git svn reset'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+       svn_cmd co "$svnrepo" s &&
+       (
+               cd s &&
+               mkdir vis &&
+               echo always visible > vis/vis.txt &&
+               svn_cmd add vis &&
+               svn_cmd commit -m "create visible files" &&
+               mkdir hid &&
+               echo initially hidden > hid/hid.txt &&
+               svn_cmd add hid &&
+               svn_cmd commit -m "create initially hidden files" &&
+               svn_cmd up &&
+               echo mod >> vis/vis.txt &&
+               svn_cmd commit -m "modify vis" &&
+               svn_cmd up
+       )
+'
+
+test_expect_success 'clone SVN repository with hidden directory' '
+       git svn init "$svnrepo" g &&
+       ( cd g && git svn fetch --ignore-paths="^hid" )
+'
+
+test_expect_success 'modify hidden file in SVN repo' '
+       ( cd s &&
+         echo mod hidden >> hid/hid.txt &&
+         svn_cmd commit -m "modify hid" &&
+         svn_cmd up
+       )
+'
+
+test_expect_success 'fetch fails on modified hidden file' '
+       ( cd g &&
+         git svn find-rev refs/remotes/git-svn > ../expect &&
+         ! git svn fetch 2> ../errors &&
+         git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
+       fgrep "not found in commit" errors &&
+       test_cmp expect expect2
+'
+
+test_expect_success 'reset unwinds back to r1' '
+       ( cd g &&
+         git svn reset -r1 &&
+         git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
+       echo 1 >expect &&
+       test_cmp expect expect2
+'
+
+test_expect_success 'refetch succeeds not ignoring any files' '
+       ( cd g &&
+         git svn fetch &&
+         git svn rebase &&
+         fgrep "mod hidden" hid/hid.txt
+       )
+'
+
+test_done
diff --git a/t/t9141-git-svn-multiple-branches.sh b/t/t9141-git-svn-multiple-branches.sh
new file mode 100755 (executable)
index 0000000..3cd0671
--- /dev/null
@@ -0,0 +1,122 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Marc Branchaud
+#
+
+test_description='git svn multiple branch and tag paths in the svn repo'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svnrepo' '
+       mkdir   project \
+               project/trunk \
+               project/b_one \
+               project/b_two \
+               project/tags_A \
+               project/tags_B &&
+       echo 1 > project/trunk/a.file &&
+       svn_cmd import -m "$test_description" project "$svnrepo/project" &&
+       rm -rf project &&
+       svn_cmd cp -m "Branch 1" "$svnrepo/project/trunk" \
+                                "$svnrepo/project/b_one/first" &&
+       svn_cmd cp -m "Tag 1" "$svnrepo/project/trunk" \
+                             "$svnrepo/project/tags_A/1.0" &&
+       svn_cmd co "$svnrepo/project" svn_project &&
+       ( cd svn_project &&
+               echo 2 > trunk/a.file &&
+               svn_cmd ci -m "Change 1" trunk/a.file &&
+               svn_cmd cp -m "Branch 2" "$svnrepo/project/trunk" \
+                                        "$svnrepo/project/b_one/second" &&
+               svn_cmd cp -m "Tag 2" "$svnrepo/project/trunk" \
+                                     "$svnrepo/project/tags_A/2.0" &&
+               echo 3 > trunk/a.file &&
+               svn_cmd ci -m "Change 2" trunk/a.file &&
+               svn_cmd cp -m "Branch 3" "$svnrepo/project/trunk" \
+                                        "$svnrepo/project/b_two/1" &&
+               svn_cmd cp -m "Tag 3" "$svnrepo/project/trunk" \
+                                     "$svnrepo/project/tags_A/3.0" &&
+               echo 4 > trunk/a.file &&
+               svn_cmd ci -m "Change 3" trunk/a.file &&
+               svn_cmd cp -m "Branch 4" "$svnrepo/project/trunk" \
+                                        "$svnrepo/project/b_two/2" &&
+               svn_cmd cp -m "Tag 4" "$svnrepo/project/trunk" \
+                                     "$svnrepo/project/tags_A/4.0" &&
+               svn_cmd up &&
+               echo 5 > b_one/first/a.file &&
+               svn_cmd ci -m "Change 4" b_one/first/a.file &&
+               svn_cmd cp -m "Tag 5" "$svnrepo/project/b_one/first" \
+                                     "$svnrepo/project/tags_B/v5" &&
+               echo 6 > b_one/second/a.file &&
+               svn_cmd ci -m "Change 5" b_one/second/a.file &&
+               svn_cmd cp -m "Tag 6" "$svnrepo/project/b_one/second" \
+                                     "$svnrepo/project/tags_B/v6" &&
+               echo 7 > b_two/1/a.file &&
+               svn_cmd ci -m "Change 6" b_two/1/a.file &&
+               svn_cmd cp -m "Tag 7" "$svnrepo/project/b_two/1" \
+                                     "$svnrepo/project/tags_B/v7" &&
+               echo 8 > b_two/2/a.file &&
+               svn_cmd ci -m "Change 7" b_two/2/a.file &&
+               svn_cmd cp -m "Tag 8" "$svnrepo/project/b_two/2" \
+                                     "$svnrepo/project/tags_B/v8"
+       )
+'
+
+test_expect_success 'clone multiple branch and tag paths' '
+       git svn clone -T trunk \
+                     -b b_one/* --branches b_two/* \
+                     -t tags_A/* --tags tags_B \
+                     "$svnrepo/project" git_project &&
+       ( cd git_project &&
+               git rev-parse refs/remotes/first &&
+               git rev-parse refs/remotes/second &&
+               git rev-parse refs/remotes/1 &&
+               git rev-parse refs/remotes/2 &&
+               git rev-parse refs/remotes/tags/1.0 &&
+               git rev-parse refs/remotes/tags/2.0 &&
+               git rev-parse refs/remotes/tags/3.0 &&
+               git rev-parse refs/remotes/tags/4.0 &&
+               git rev-parse refs/remotes/tags/v5 &&
+               git rev-parse refs/remotes/tags/v6 &&
+               git rev-parse refs/remotes/tags/v7 &&
+               git rev-parse refs/remotes/tags/v8
+       )
+'
+
+test_expect_success 'Multiple branch or tag paths require -d' '
+       ( cd git_project &&
+               test_must_fail git svn branch -m "No new branch" Nope &&
+               test_must_fail git svn tag -m "No new tag" Tagless &&
+               test_must_fail git rev-parse refs/remotes/Nope &&
+               test_must_fail git rev-parse refs/remotes/tags/Tagless
+       ) &&
+       ( cd svn_project &&
+               svn_cmd up &&
+               test_must_fail test -d b_one/Nope &&
+               test_must_fail test -d b_two/Nope &&
+               test_must_fail test -d tags_A/Tagless &&
+               test_must_fail test -d tags_B/Tagless
+       )
+'
+
+test_expect_success 'create new branches and tags' '
+       ( cd git_project &&
+               git svn branch -m "New branch 1" -d b_one New1 ) &&
+       ( cd svn_project &&
+               svn_cmd up && test -e b_one/New1/a.file ) &&
+
+       ( cd git_project &&
+               git svn branch -m "New branch 2" -d b_two New2 ) &&
+       ( cd svn_project &&
+               svn_cmd up && test -e b_two/New2/a.file ) &&
+
+       ( cd git_project &&
+               git svn branch -t -m "New tag 1" -d tags_A Tag1 ) &&
+       ( cd svn_project &&
+               svn_cmd up && test -e tags_A/Tag1/a.file ) &&
+
+       ( cd git_project &&
+               git svn tag -m "New tag 2" -d tags_B Tag2 ) &&
+       ( cd svn_project &&
+               svn_cmd up && test -e tags_B/Tag2/a.file )
+'
+
+test_done
diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh
new file mode 100755 (executable)
index 0000000..1236acc
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+
+test_description='git svn shallow clone'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+       svn_cmd mkdir -m "create standard layout" \
+         "$svnrepo"/trunk "$svnrepo"/branches "$svnrepo"/tags &&
+       svn_cmd cp -m "branch off trunk" \
+         "$svnrepo"/trunk "$svnrepo"/branches/a &&
+       svn_cmd co "$svnrepo"/branches/a &&
+       (
+               cd a &&
+               > foo &&
+               svn_cmd add foo &&
+               svn_cmd commit -m "add foo"
+       )
+'
+
+start_httpd
+
+test_expect_success 'clone trunk with "-r HEAD"' '
+       git svn clone -r HEAD "$svnrepo/trunk" g &&
+       ( cd g && git rev-parse --symbolic --verify HEAD )
+'
+
+stop_httpd
+
+test_done
diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh
new file mode 100755 (executable)
index 0000000..99f69c6
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Robert Allan Zeh
+
+test_description='git svn gc basic tests'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup directories and test repo' '
+       mkdir import &&
+       mkdir tmp &&
+       echo "Sample text for Subversion repository." > import/test.txt &&
+       svn_cmd import -m "import for git svn" import "$svnrepo" > /dev/null
+       '
+
+test_expect_success 'checkout working copy from svn' \
+       'svn_cmd co "$svnrepo" test_wc'
+
+test_expect_success 'set some properties to create an unhandled.log file' '
+       (
+               cd test_wc &&
+               svn_cmd propset foo bar test.txt &&
+               svn_cmd commit -m "property set"
+       )'
+
+test_expect_success 'Setup repo' 'git svn init "$svnrepo"'
+
+test_expect_success 'Fetch repo' 'git svn fetch'
+
+test_expect_success 'make backup copy of unhandled.log' '
+        cp .git/svn/refs/remotes/git-svn/unhandled.log tmp
+       '
+
+test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index'
+
+test_expect_success 'git svn gc runs' 'git svn gc'
+
+test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index'
+
+if perl -MCompress::Zlib -e 0 2>/dev/null
+then
+       test_expect_success 'git svn gc produces a valid gzip file' '
+                gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz
+               '
+else
+       say "Perl Compress::Zlib unavailable, skipping gunzip test"
+fi
+
+test_expect_success 'git svn gc does not change unhandled.log files' '
+        test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log
+       '
+
+test_done
diff --git a/t/t9144-git-svn-old-rev_map.sh b/t/t9144-git-svn-old-rev_map.sh
new file mode 100755 (executable)
index 0000000..7600a35
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn old rev_map preservd'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository with old layout' '
+       mkdir i &&
+       (cd i && > a) &&
+       svn_cmd import -m- i "$svnrepo" &&
+       git svn init "$svnrepo" &&
+       git svn fetch &&
+       test -d .git/svn/refs/remotes/git-svn/ &&
+       ! test -e .git/svn/git-svn/ &&
+       mv .git/svn/refs/remotes/git-svn .git/svn/ &&
+       rm -r .git/svn/refs
+'
+
+test_expect_success 'old layout continues to work' '
+       svn_cmd import -m- i "$svnrepo/b" &&
+       git svn rebase &&
+       echo a >> b/a &&
+       git add b/a &&
+       git commit -m- -a &&
+       git svn dcommit &&
+       ! test -d .git/svn/refs/ &&
+       test -e .git/svn/git-svn/
+'
+
+test_done
diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh
new file mode 100755 (executable)
index 0000000..16852d2
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+test_description='git svn initial master branch is "trunk" if possible'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+       mkdir i &&
+       > i/a &&
+       svn_cmd import -m trunk i "$svnrepo/trunk" &&
+       svn_cmd import -m b/a i "$svnrepo/branches/a" &&
+       svn_cmd import -m b/b i "$svnrepo/branches/b"
+'
+
+test_expect_success 'git svn clone --stdlayout sets up trunk as master' '
+       git svn clone -s "$svnrepo" g &&
+       (
+               cd g &&
+               test x`git rev-parse --verify refs/remotes/trunk^0` = \
+                    x`git rev-parse --verify refs/heads/master^0`
+       )
+'
+
+test_done
index 56b7c06921d9ef3b72ff3ee6f62f7a1c426b3028..fc3795dc98803bd98e2ebd6f38a249c331038d54 100755 (executable)
@@ -288,6 +288,27 @@ test_expect_success 'check files before directories' '
 
 '
 
+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"
+'
+
+# the state of the CVS sandbox may be indeterminate for ' space'
+# after this test on some platforms / with some versions of CVS
+# consider adding new tests above this point
 test_expect_success 'commit a file with leading spaces in the name' '
 
        echo space > " space" &&
@@ -295,7 +316,7 @@ test_expect_success 'commit a file with leading spaces in the name' '
        git commit -m "Add a file with a leading space" &&
        id=$(git rev-parse HEAD) &&
        git cvsexportcommit -w "$CVSWORK" -c $id &&
-       check_entries "$CVSWORK" " space/1.1/|DS/1.1/|release-notes/1.2/" &&
+       check_entries "$CVSWORK" " space/1.1/|DS/1.1/|attic_gremlin/1.3/|release-notes/1.2/" &&
        test_cmp "$CVSWORK/ space" " space"
 
 '
index 8da9ce54596416af6fc9779a5ac3f7131958aec8..356964e53a1acba1558881865fd99acdee48a17f 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 &&
@@ -262,6 +262,94 @@ test_expect_success 'cope with tagger-less tags' '
 
 '
 
+test_expect_success 'setup for limiting exports by PATH' '
+       mkdir limit-by-paths &&
+       cd limit-by-paths &&
+       git init &&
+       echo hi > there &&
+       git add there &&
+       git commit -m "First file" &&
+       echo foo > bar &&
+       git add bar &&
+       git commit -m "Second file" &&
+       git tag -a -m msg mytag &&
+       echo morefoo >> bar &&
+       git add bar &&
+       git commit -m "Change to second file" &&
+       cd ..
+'
+
+cat > limit-by-paths/expected << EOF
+blob
+mark :1
+data 3
+hi
+
+reset refs/tags/mytag
+commit refs/tags/mytag
+mark :2
+author A U Thor <author@example.com> 1112912713 -0700
+committer C O Mitter <committer@example.com> 1112912713 -0700
+data 11
+First file
+M 100644 :1 there
+
+EOF
+
+test_expect_success 'dropping tag of filtered out object' '
+       cd limit-by-paths &&
+       git fast-export --tag-of-filtered-object=drop mytag -- there > output &&
+       test_cmp output expected &&
+       cd ..
+'
+
+cat >> limit-by-paths/expected << EOF
+tag mytag
+from :2
+tagger C O Mitter <committer@example.com> 1112912713 -0700
+data 4
+msg
+
+EOF
+
+test_expect_success 'rewriting tag of filtered out object' '
+       cd limit-by-paths &&
+       git fast-export --tag-of-filtered-object=rewrite mytag -- there > output &&
+       test_cmp output expected &&
+       cd ..
+'
+
+cat > limit-by-paths/expected << EOF
+blob
+mark :1
+data 4
+foo
+
+blob
+mark :2
+data 3
+hi
+
+reset refs/heads/master
+commit refs/heads/master
+mark :3
+author A U Thor <author@example.com> 1112912713 -0700
+committer C O Mitter <committer@example.com> 1112912713 -0700
+data 12
+Second file
+M 100644 :1 bar
+M 100644 :2 there
+
+EOF
+
+test_expect_failure 'no exact-ref revisions included' '
+       cd limit-by-paths &&
+       git fast-export master~2..master~1 > output &&
+       test_cmp output expected &&
+       cd ..
+'
+
+
 test_expect_success 'set-up a few more tags for tag export tests' '
        git checkout -f master &&
        HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
@@ -271,8 +359,14 @@ test_expect_success 'set-up a few more tags for tag export tests' '
        git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
 '
 
+test_expect_success 'tree_tag'        '
+       mkdir result &&
+       (cd result && git init) &&
+       git fast-export tree_tag > fe-stream &&
+       (cd result && git fast-import < ../fe-stream)
+'
+
 # NEEDSWORK: not just check return status, but validate the output
-test_expect_success 'tree_tag'        'git fast-export tree_tag'
 test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'
 test_expect_success 'tag-obj_tag'     'git fast-export tag-obj_tag'
 test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
index f4210fbb04065cfe32f26053eb5f5f054d52e3cf..2fc7fdb124583f86d5be622510f29ceca1dd3e09 100755 (executable)
@@ -9,73 +9,8 @@ This test runs gitweb (git web interface) as CGI script from
 commandline, and checks that it would not write any errors
 or warnings to log.'
 
-gitweb_init () {
-       safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
-       cat >gitweb_config.perl <<EOF
-#!/usr/bin/perl
-
-# gitweb configuration for tests
-
-our \$version = "current";
-our \$GIT = "git";
-our \$projectroot = "$safe_pwd";
-our \$project_maxdepth = 8;
-our \$home_link_str = "projects";
-our \$site_name = "[localhost]";
-our \$site_header = "";
-our \$site_footer = "";
-our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css");
-our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png";
-our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png";
-our \$projects_list = "";
-our \$export_ok = "";
-our \$strict_export = "";
 
-EOF
-
-       cat >.git/description <<EOF
-$0 test repository
-EOF
-}
-
-gitweb_run () {
-       GATEWAY_INTERFACE="CGI/1.1"
-       HTTP_ACCEPT="*/*"
-       REQUEST_METHOD="GET"
-       SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
-       QUERY_STRING=""$1""
-       PATH_INFO=""$2""
-       export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
-               SCRIPT_NAME QUERY_STRING PATH_INFO
-
-       GITWEB_CONFIG=$(pwd)/gitweb_config.perl
-       export GITWEB_CONFIG
-
-       # some of git commands write to STDERR on error, but this is not
-       # written to web server logs, so we are not interested in that:
-       # we are interested only in properly formatted errors/warnings
-       rm -f gitweb.log &&
-       perl -- "$SCRIPT_NAME" \
-               >/dev/null 2>gitweb.log &&
-       if grep "^[[]" gitweb.log >/dev/null 2>&1; then false; else true; fi
-
-       # gitweb.log is left for debugging
-}
-
-. ./test-lib.sh
-
-if ! test_have_prereq PERL; then
-       say 'skipping gitweb tests, perl not available'
-       test_done
-fi
-
-perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
-    say 'skipping gitweb tests, perl version is too old'
-    test_done
-}
-
-gitweb_init
+. ./gitweb-lib.sh
 
 # ----------------------------------------------------------------------
 # no commits (empty, just initialized repository)
@@ -590,7 +525,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'
@@ -660,6 +595,7 @@ cat >>gitweb_config.perl <<EOF
 
 \$feature{'blame'}{'override'} = 1;
 \$feature{'snapshot'}{'override'} = 1;
+\$feature{'avatar'}{'override'} = 1;
 EOF
 
 test_expect_success \
@@ -671,6 +607,7 @@ test_expect_success \
        'config override: tree view, features disabled in repo config' \
        'git config gitweb.blame no &&
         git config gitweb.snapshot none &&
+        git config gitweb.avatar gravatar &&
         gitweb_run "p=.git;a=tree"'
 test_debug 'cat gitweb.log'
 
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
new file mode 100755 (executable)
index 0000000..d0ff21d
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Mark Rada
+#
+
+test_description='gitweb as standalone script (http status tests).
+
+This test runs gitweb (git web interface) as a CGI script from the
+commandline, and checks that it returns the expected HTTP status
+code and message.'
+
+
+. ./gitweb-lib.sh
+
+# ----------------------------------------------------------------------
+# snapshot settings
+
+test_commit \
+       'SnapshotTests' \
+       'i can has snapshot?'
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'override'} = 0;
+EOF
+
+test_expect_success \
+    'snapshots: tgz only default format enabled' \
+    'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+    grep "Status: 200 OK" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+    grep "403 - Unsupported snapshot format" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+    grep "403 - Snapshot format not allowed" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+    grep "403 - Unsupported snapshot format" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'default'} = ['tgz','tbz2','txz','zip'];
+EOF
+
+test_expect_success \
+    'snapshots: all enabled in default, use default disabled value' \
+    'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+    grep "Status: 200 OK" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+    grep "Status: 200 OK" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+    grep "403 - Snapshot format not allowed" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+    grep "Status: 200 OK" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'zip'}{'disabled'} = 1;
+EOF
+
+test_expect_success \
+    'snapshots: zip explicitly disabled' \
+    'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+    grep "403 - Snapshot format not allowed" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'tgz'}{'disabled'} = 0;
+EOF
+
+test_expect_success \
+    'snapshots: tgz explicitly enabled' \
+    'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+    grep "Status: 200 OK" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+test_done
index 4322a0c1ed6d792cbba4b52a7ba8bad74986bbad..363345faef7b1eb209c548914b94460d9475cb13 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 test_description='git cvsimport basic tests'
-. ./test-lib.sh
+. ./lib-cvs.sh
 
 if ! test_have_prereq PERL; then
        say 'skipping git cvsimport tests, perl not available'
@@ -10,37 +10,13 @@ fi
 
 CVSROOT=$(pwd)/cvsroot
 export CVSROOT
-unset CVS_SERVER
-# for clean cvsps cache
-HOME=$(pwd)
-export HOME
-
-if ! type cvs >/dev/null 2>&1
-then
-       say 'skipping cvsimport tests, cvs not found'
-       test_done
-fi
-
-cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
-case "$cvsps_version" in
-2.1 | 2.2*)
-       ;;
-'')
-       say 'skipping cvsimport tests, cvsps not found'
-       test_done
-       ;;
-*)
-       say 'skipping cvsimport tests, unsupported cvsps version'
-       test_done
-       ;;
-esac
 
-test_expect_success 'setup cvsroot' 'cvs init'
+test_expect_success 'setup cvsroot' '$CVS init'
 
 test_expect_success 'setup a cvs module' '
 
        mkdir "$CVSROOT/module" &&
-       cvs co -d module-cvs module &&
+       $CVS co -d module-cvs module &&
        cd module-cvs &&
        cat <<EOF >o_fortuna &&
 O Fortuna
@@ -59,13 +35,13 @@ egestatem,
 potestatem
 dissolvit ut glaciem.
 EOF
-       cvs add o_fortuna &&
+       $CVS add o_fortuna &&
        cat <<EOF >message &&
 add "O Fortuna" lyrics
 
 These public domain lyrics make an excellent sample text.
 EOF
-       cvs commit -F message &&
+       $CVS commit -F message &&
        cd ..
 '
 
@@ -103,7 +79,7 @@ translate to English
 
 My Latin is terrible.
 EOF
-       cvs commit -F message &&
+       $CVS commit -F message &&
        cd ..
 '
 
@@ -121,8 +97,8 @@ test_expect_success 'update cvs module' '
 
        cd module-cvs &&
                echo 1 >tick &&
-               cvs add tick &&
-               cvs commit -m 1
+               $CVS add tick &&
+               $CVS commit -m 1
        cd ..
 
 '
@@ -140,7 +116,7 @@ test_expect_success 'cvsimport.module config works' '
 
 test_expect_success 'import from a CVS working tree' '
 
-       cvs co -d import-from-wt module &&
+       $CVS co -d import-from-wt module &&
        cd import-from-wt &&
                git cvsimport -a -z0 &&
                echo 1 >expect &&
@@ -150,4 +126,6 @@ test_expect_success 'import from a CVS working tree' '
 
 '
 
+test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master'
+
 test_done
diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh
new file mode 100755 (executable)
index 0000000..3afaf56
--- /dev/null
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+# Description of the files in the repository:
+#
+#    imported-once.txt:
+#
+#       Imported once.  1.1 and 1.1.1.1 should be identical.
+#
+#    imported-twice.txt:
+#
+#       Imported twice.  HEAD should reflect the contents of the
+#       second import (i.e., have the same contents as 1.1.1.2).
+#
+#    imported-modified.txt:
+#
+#       Imported, then modified on HEAD.  HEAD should reflect the
+#       modification.
+#
+#    imported-modified-imported.txt:
+#
+#       Imported, then modified on HEAD, then imported again.
+#
+#    added-imported.txt,v:
+#
+#       Added with 'cvs add' to create 1.1, then imported with
+#       completely different contents to create 1.1.1.1, therefore the
+#       vendor branch was never the default branch.
+#
+#    imported-anonymously.txt:
+#
+#       Like imported-twice.txt, but with a vendor branch whose branch
+#       tag has been removed.
+
+test_description='git cvsimport handling of vendor branches'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot
+export CVSROOT
+
+test_expect_success 'import a module with a vendor branch' '
+
+       git cvsimport -C module-git module
+
+'
+
+test_expect_success 'check HEAD out of cvs repository' 'test_cvs_co master'
+
+test_expect_success 'check master out of git repository' 'test_git_co master'
+
+test_expect_success 'check a file that was imported once' '
+
+       test_cmp_branch_file master imported-once.txt
+
+'
+
+test_expect_failure 'check a file that was imported twice' '
+
+       test_cmp_branch_file master imported-twice.txt
+
+'
+
+test_expect_success 'check a file that was imported then modified on HEAD' '
+
+       test_cmp_branch_file master imported-modified.txt
+
+'
+
+test_expect_success 'check a file that was imported, modified, then imported again' '
+
+       test_cmp_branch_file master imported-modified-imported.txt
+
+'
+
+test_expect_success 'check a file that was added to HEAD then imported' '
+
+       test_cmp_branch_file master added-imported.txt
+
+'
+
+test_expect_success 'a vendor branch whose tag has been removed' '
+
+       test_cmp_branch_file master imported-anonymously.txt
+
+'
+
+test_done
diff --git a/t/t9601/cvsroot/.gitattributes b/t/t9601/cvsroot/.gitattributes
new file mode 100644 (file)
index 0000000..562b12e
--- /dev/null
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9601/cvsroot/CVSROOT/.gitignore b/t/t9601/cvsroot/CVSROOT/.gitignore
new file mode 100644 (file)
index 0000000..3bb9b34
--- /dev/null
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9601/cvsroot/module/added-imported.txt,v b/t/t9601/cvsroot/module/added-imported.txt,v
new file mode 100644 (file)
index 0000000..5f83072
--- /dev/null
@@ -0,0 +1,44 @@
+head   1.1;
+access;
+symbols
+       vtag-4:1.1.1.1
+       vbranchA:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.1
+date   2004.02.09.15.43.15;    author kfogel;  state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2004.02.09.15.43.16;    author kfogel;  state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Add a file to the working copy.
+@
+text
+@Adding this file, before importing it with different contents.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-4).
+@
+text
+@d1 1
+a1 1
+This is vtag-4 (on vbranchA) of added-then-imported.txt.
+@
+
diff --git a/t/t9601/cvsroot/module/imported-anonymously.txt,v b/t/t9601/cvsroot/module/imported-anonymously.txt,v
new file mode 100644 (file)
index 0000000..55e1b0c
--- /dev/null
@@ -0,0 +1,42 @@
+head   1.1;
+branch 1.1.1;
+access;
+symbols
+       vtag-1:1.1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-anonymously.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-modified-imported.txt,v b/t/t9601/cvsroot/module/imported-modified-imported.txt,v
new file mode 100644 (file)
index 0000000..e5830ae
--- /dev/null
@@ -0,0 +1,76 @@
+head   1.2;
+access;
+symbols
+       vtag-2:1.1.1.2
+       vtag-1:1.1.1.1
+       vbranchA:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.2
+date   2004.02.09.15.43.14;    author kfogel;  state Exp;
+branches;
+next   1.1;
+
+1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches;
+next   1.1.1.2;
+
+1.1.1.2
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.2
+log
+@First regular commit, to imported-modified-imported.txt, on HEAD.
+@
+text
+@This is a modification of imported-modified-imported.txt on HEAD.
+It should supersede the version from the vendor branch.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d1 2
+a2 1
+This is vtag-1 (on vbranchA) of imported-modified-imported.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
+1.1.1.2
+log
+@Import (vbranchA, vtag-2).
+@
+text
+@d1 1
+a1 1
+This is vtag-2 (on vbranchA) of imported-modified-imported.txt.
+@
+
+
diff --git a/t/t9601/cvsroot/module/imported-modified.txt,v b/t/t9601/cvsroot/module/imported-modified.txt,v
new file mode 100644 (file)
index 0000000..bbcfe44
--- /dev/null
@@ -0,0 +1,59 @@
+head   1.2;
+access;
+symbols
+       vtag-1:1.1.1.1
+       vbranchA:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.2
+date   2004.02.09.15.43.14;    author kfogel;  state Exp;
+branches;
+next   1.1;
+
+1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Commit on HEAD.
+@
+text
+@This is a modification of imported-modified.txt on HEAD.
+It should supersede the version from the vendor branch.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d1 2
+a2 1
+This is vtag-1 (on vbranchA) of imported-modified.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-once.txt,v b/t/t9601/cvsroot/module/imported-once.txt,v
new file mode 100644 (file)
index 0000000..c5dd82b
--- /dev/null
@@ -0,0 +1,43 @@
+head   1.1;
+branch 1.1.1;
+access;
+symbols
+       vtag-1:1.1.1.1
+       vbranchA:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-once.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-twice.txt,v b/t/t9601/cvsroot/module/imported-twice.txt,v
new file mode 100644 (file)
index 0000000..d1f3f1b
--- /dev/null
@@ -0,0 +1,60 @@
+head   1.1;
+branch 1.1.1;
+access;
+symbols
+       vtag-2:1.1.1.2
+       vtag-1:1.1.1.1
+       vbranchA:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches;
+next   1.1.1.2;
+
+1.1.1.2
+date   2004.02.09.15.43.13;    author kfogel;  state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-twice.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
+1.1.1.2
+log
+@Import (vbranchA, vtag-2).
+@
+text
+@d1 1
+a1 1
+This is vtag-2 (on vbranchA) of imported-twice.txt.
+@
+
+
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
new file mode 100755 (executable)
index 0000000..67878b2
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# A description of the repository used for this test can be found in
+# t9602/README.
+
+test_description='git cvsimport handling of branches and tags'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot
+export CVSROOT
+
+test_expect_success 'import module' '
+
+       git cvsimport -C module-git module
+
+'
+
+test_expect_success 'test branch master' '
+
+       test_cmp_branch_tree master
+
+'
+
+test_expect_success 'test branch vendorbranch' '
+
+       test_cmp_branch_tree vendorbranch
+
+'
+
+test_expect_failure 'test branch B_FROM_INITIALS' '
+
+       test_cmp_branch_tree B_FROM_INITIALS
+
+'
+
+test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' '
+
+       test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE
+
+'
+
+test_expect_failure 'test branch B_MIXED' '
+
+       test_cmp_branch_tree B_MIXED
+
+'
+
+test_expect_success 'test branch B_SPLIT' '
+
+       test_cmp_branch_tree B_SPLIT
+
+'
+
+test_expect_failure 'test tag vendortag' '
+
+       test_cmp_branch_tree vendortag
+
+'
+
+test_expect_success 'test tag T_ALL_INITIAL_FILES' '
+
+       test_cmp_branch_tree T_ALL_INITIAL_FILES
+
+'
+
+test_expect_failure 'test tag T_ALL_INITIAL_FILES_BUT_ONE' '
+
+       test_cmp_branch_tree T_ALL_INITIAL_FILES_BUT_ONE
+
+'
+
+test_expect_failure 'test tag T_MIXED' '
+
+       test_cmp_branch_tree T_MIXED
+
+'
+
+
+test_done
diff --git a/t/t9602/README b/t/t9602/README
new file mode 100644 (file)
index 0000000..c231e0f
--- /dev/null
@@ -0,0 +1,62 @@
+This repository is for testing the ability to group revisions
+correctly along tags and branches.  Here is its history:
+
+  1.  The initial import (revision 1.1 of everybody) created a
+      directory structure with a file named `default' in each dir:
+
+            ./
+              default
+              sub1/default
+                   subsubA/default
+                   subsubB/default
+              sub2/default
+                   subsubA/default
+              sub3/default
+
+  2.  Then tagged everyone with T_ALL_INITIAL_FILES.
+
+  3.  Then tagged everyone except sub1/subsubB/default with
+      T_ALL_INITIAL_FILES_BUT_ONE.
+
+  4.  Then created branch B_FROM_INITIALS on everyone.
+
+  5.  Then created branch B_FROM_INITIALS_BUT_ONE on everyone except
+      /sub1/subsubB/default.
+
+  6.  Then committed modifications to two files: sub3/default, and
+      sub1/subsubA/default.
+
+  7.  Then committed a modification to all 7 files.
+
+  8.  Then backdated sub3/default to revision 1.2, and
+      sub2/subsubA/default to revision 1.1, and tagged with T_MIXED.
+
+  9.  Same as 8, but tagged with -b to create branch B_MIXED.
+
+  10. Switched the working copy to B_MIXED, and added
+      sub2/branch_B_MIXED_only.  (That's why the RCS file is in
+      sub2/Attic/ -- it never existed on trunk.)
+
+  11. In one commit, modified default, sub1/default, and
+      sub2/subsubA/default, on branch B_MIXED.
+
+  12. Did "cvs up -A" on sub2/default, then in one commit, made a
+      change to sub2/default and sub2/branch_B_MIXED_only.  So this
+      commit should be spread between the branch and the trunk.
+
+  13. Do "cvs up -A" to get everyone back to trunk, then make a new
+      branch B_SPLIT on everyone except sub1/subsubB/default,v.
+
+  14. Switch to branch B_SPLIT (see sub1/subsubB/default disappear)
+      and commit a change that affects everyone except sub3/default.
+
+  15. An hour or so later, "cvs up -A" to get sub1/subsubB/default
+      back, then commit a change on that file, on trunk.  (It's
+      important that this change happened after the previous commits
+      on B_SPLIT.)
+
+  16. Branch sub1/subsubB/default to B_SPLIT, then "cvs up -r B_SPLIT"
+      to switch the whole working copy to the branch.
+
+  17. Commit a change on B_SPLIT, to sub1/subsubB/default and
+      sub3/default.
diff --git a/t/t9602/cvsroot/.gitattributes b/t/t9602/cvsroot/.gitattributes
new file mode 100644 (file)
index 0000000..562b12e
--- /dev/null
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9602/cvsroot/CVSROOT/.gitignore b/t/t9602/cvsroot/CVSROOT/.gitignore
new file mode 100644 (file)
index 0000000..3bb9b34
--- /dev/null
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9602/cvsroot/module/default,v b/t/t9602/cvsroot/module/default,v
new file mode 100644 (file)
index 0000000..3b68382
--- /dev/null
@@ -0,0 +1,102 @@
+head   1.2;
+access;
+symbols
+       B_SPLIT:1.2.0.4
+       B_MIXED:1.2.0.2
+       T_MIXED:1.2
+       B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+       B_FROM_INITIALS:1.1.1.1.0.2
+       T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+       T_ALL_INITIAL_FILES:1.1.1.1
+       vendortag:1.1.1.1
+       vendorbranch:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.2
+date   2003.05.23.00.17.53;    author jrandom; state Exp;
+branches
+       1.2.2.1
+       1.2.4.1;
+next   1.1;
+
+1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.2.2.1
+date   2003.05.23.00.31.36;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.2.4.1
+date   2003.06.03.03.20.31;    author jrandom; state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is the file `default' in the top level of the project.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a5 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/default,v b/t/t9602/cvsroot/module/sub1/default,v
new file mode 100644 (file)
index 0000000..b7fdccd
--- /dev/null
@@ -0,0 +1,102 @@
+head   1.2;
+access;
+symbols
+       B_SPLIT:1.2.0.4
+       B_MIXED:1.2.0.2
+       T_MIXED:1.2
+       B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+       B_FROM_INITIALS:1.1.1.1.0.2
+       T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+       T_ALL_INITIAL_FILES:1.1.1.1
+       vendortag:1.1.1.1
+       vendorbranch:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.2
+date   2003.05.23.00.17.53;    author jrandom; state Exp;
+branches
+       1.2.2.1
+       1.2.4.1;
+next   1.1;
+
+1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.2.2.1
+date   2003.05.23.00.31.36;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.2.4.1
+date   2003.06.03.03.20.31;    author jrandom; state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub1/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a5 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/subsubA/default,v b/t/t9602/cvsroot/module/sub1/subsubA/default,v
new file mode 100644 (file)
index 0000000..472b7b2
--- /dev/null
@@ -0,0 +1,101 @@
+head   1.3;
+access;
+symbols
+       B_SPLIT:1.3.0.4
+       B_MIXED:1.3.0.2
+       T_MIXED:1.3
+       B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+       B_FROM_INITIALS:1.1.1.1.0.2
+       T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+       T_ALL_INITIAL_FILES:1.1.1.1
+       vendortag:1.1.1.1
+       vendorbranch:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.3
+date   2003.05.23.00.17.53;    author jrandom; state Exp;
+branches
+       1.3.4.1;
+next   1.2;
+
+1.2
+date   2003.05.23.00.15.26;    author jrandom; state Exp;
+branches;
+next   1.1;
+
+1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.3.4.1
+date   2003.06.03.03.20.31;    author jrandom; state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub1/subsubA/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added by the first commit (affecting two files).
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.3.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a7 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2
+log
+@First commit to proj, affecting two files.
+@
+text
+@d6 2
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/subsubB/default,v b/t/t9602/cvsroot/module/sub1/subsubB/default,v
new file mode 100644 (file)
index 0000000..fe6efa4
--- /dev/null
@@ -0,0 +1,107 @@
+head   1.3;
+access;
+symbols
+       B_SPLIT:1.3.0.2
+       B_MIXED:1.2.0.2
+       T_MIXED:1.2
+       B_FROM_INITIALS:1.1.1.1.0.2
+       T_ALL_INITIAL_FILES:1.1.1.1
+       vendortag:1.1.1.1
+       vendorbranch:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.3
+date   2003.06.03.04.29.14;    author jrandom; state Exp;
+branches
+       1.3.2.1;
+next   1.2;
+
+1.2
+date   2003.05.23.00.17.53;    author jrandom; state Exp;
+branches;
+next   1.1;
+
+1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.3.2.1
+date   2003.06.03.04.33.13;    author jrandom; state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.3
+log
+@A trunk change to sub1/subsubB/default.  This was committed about an
+hour after an earlier change that affected most files on branch
+B_SPLIT.  This file is not on that branch yet, but after this commit,
+we'll branch to B_SPLIT, albeit rooted in a revision that didn't exist
+at the time the rest of B_SPLIT was created.
+@
+text
+@This is sub1/subsubB/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+
+This bit was committed on trunk about an hour after an earlier change
+to everyone else on branch B_SPLIT.  Afterwards, we'll finally branch
+this file to B_SPLIT, but rooted in a revision that didn't exist at
+the time the rest of B_SPLIT was created.
+@
+
+
+1.3.2.1
+log
+@This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT.  Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+text
+@a10 4
+
+This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT.  Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@d6 5
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v
new file mode 100644 (file)
index 0000000..34c9789
--- /dev/null
@@ -0,0 +1,59 @@
+head   1.1;
+access;
+symbols
+       B_MIXED:1.1.0.2;
+locks; strict;
+comment        @# @;
+
+
+1.1
+date   2003.05.23.00.25.26;    author jrandom; state dead;
+branches
+       1.1.2.1;
+next   ;
+
+1.1.2.1
+date   2003.05.23.00.25.26;    author jrandom; state Exp;
+branches;
+next   1.1.2.2;
+
+1.1.2.2
+date   2003.05.23.00.48.51;    author jrandom; state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.1
+log
+@file branch_B_MIXED_only was initially added on branch B_MIXED.
+@
+text
+@@
+
+
+1.1.2.1
+log
+@Add a file on branch B_MIXED.
+@
+text
+@a0 1
+This file was added on branch B_MIXED.  It never existed on trunk.
+@
+
+
+1.1.2.2
+log
+@A single commit affecting one file on branch B_MIXED and one on trunk.
+@
+text
+@a1 3
+
+The same commit added these two lines here on branch B_MIXED, and two
+similar lines to ./default on trunk.
+@
+
+
diff --git a/t/t9602/cvsroot/module/sub2/default,v b/t/t9602/cvsroot/module/sub2/default,v
new file mode 100644 (file)
index 0000000..018f7f8
--- /dev/null
@@ -0,0 +1,102 @@
+head   1.3;
+access;
+symbols
+       B_SPLIT:1.3.0.2
+       B_MIXED:1.2.0.2
+       T_MIXED:1.2
+       B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+       B_FROM_INITIALS:1.1.1.1.0.2
+       T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+       T_ALL_INITIAL_FILES:1.1.1.1
+       vendortag:1.1.1.1
+       vendorbranch:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.3
+date   2003.05.23.00.48.51;    author jrandom; state Exp;
+branches
+       1.3.2.1;
+next   1.2;
+
+1.2
+date   2003.05.23.00.17.53;    author jrandom; state Exp;
+branches;
+next   1.1;
+
+1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.3.2.1
+date   2003.06.03.03.20.31;    author jrandom; state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.3
+log
+@A single commit affecting one file on branch B_MIXED and one on trunk.
+@
+text
+@This is sub2/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+
+The same commit added these two lines here on trunk, and two similar
+lines to ./branch_B_MIXED_only on branch B_MIXED.
+@
+
+
+1.3.2.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a8 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@d6 3
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub2/subsubA/default,v b/t/t9602/cvsroot/module/sub2/subsubA/default,v
new file mode 100644 (file)
index 0000000..d13242c
--- /dev/null
@@ -0,0 +1,102 @@
+head   1.2;
+access;
+symbols
+       B_SPLIT:1.2.0.2
+       B_MIXED:1.1.0.2
+       T_MIXED:1.1
+       B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+       B_FROM_INITIALS:1.1.1.1.0.2
+       T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+       T_ALL_INITIAL_FILES:1.1.1.1
+       vendortag:1.1.1.1
+       vendorbranch:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.2
+date   2003.05.23.00.17.53;    author jrandom; state Exp;
+branches
+       1.2.2.1;
+next   1.1;
+
+1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches
+       1.1.1.1
+       1.1.2.1;
+next   ;
+
+1.1.1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.1.2.1
+date   2003.05.23.00.31.36;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.2.2.1
+date   2003.06.03.03.20.31;    author jrandom; state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub2/subsub2/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.2.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a3 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub3/default,v b/t/t9602/cvsroot/module/sub3/default,v
new file mode 100644 (file)
index 0000000..88e4567
--- /dev/null
@@ -0,0 +1,102 @@
+head   1.3;
+access;
+symbols
+       B_SPLIT:1.3.0.2
+       B_MIXED:1.2.0.2
+       T_MIXED:1.2
+       B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+       B_FROM_INITIALS:1.1.1.1.0.2
+       T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+       T_ALL_INITIAL_FILES:1.1.1.1
+       vendortag:1.1.1.1
+       vendorbranch:1.1.1;
+locks; strict;
+comment        @# @;
+
+
+1.3
+date   2003.05.23.00.17.53;    author jrandom; state Exp;
+branches
+       1.3.2.1;
+next   1.2;
+
+1.2
+date   2003.05.23.00.15.26;    author jrandom; state Exp;
+branches;
+next   1.1;
+
+1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches
+       1.1.1.1;
+next   ;
+
+1.1.1.1
+date   2003.05.22.23.20.19;    author jrandom; state Exp;
+branches;
+next   ;
+
+1.3.2.1
+date   2003.06.03.04.33.13;    author jrandom; state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub3/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added by the first commit (affecting two files).
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.3.2.1
+log
+@This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT.  Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+text
+@a7 4
+
+This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT.  Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+
+
+1.2
+log
+@First commit to proj, affecting two files.
+@
+text
+@d6 2
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
new file mode 100755 (executable)
index 0000000..958bdce
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Structure of the test cvs repository
+#
+# Message   File:Content         Commit Time
+# Rev 1     a: 1.1               2009-02-21 19:11:43 +0100
+# Rev 2     a: 1.2    b: 1.1     2009-02-21 19:11:14 +0100
+# Rev 3               b: 1.2     2009-02-21 19:11:43 +0100
+#
+# As you can see the commit of Rev 3 has the same time as
+# Rev 1 this leads to a broken import because of a cvsps
+# bug.
+
+test_description='git cvsimport testing for correct patchset estimation'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot
+export CVSROOT
+
+test_expect_failure 'import with criss cross times on revisions' '
+
+    git cvsimport -p"-x" -C module-git module &&
+    cd module-git &&
+        git log --pretty=format:%s > ../actual-master &&
+        git log A~2..A --pretty="format:%s %ad" -- > ../actual-A &&
+        echo "" >> ../actual-master &&
+        echo "" >> ../actual-A &&
+    cd .. &&
+    echo "Rev 4
+Rev 3
+Rev 2
+Rev 1" > expect-master &&
+    test_cmp actual-master expect-master &&
+
+    echo "Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000
+Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000" > expect-A &&
+    test_cmp actual-A expect-A
+'
+
+test_done
diff --git a/t/t9603/cvsroot/.gitattributes b/t/t9603/cvsroot/.gitattributes
new file mode 100644 (file)
index 0000000..562b12e
--- /dev/null
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9603/cvsroot/CVSROOT/.gitignore b/t/t9603/cvsroot/CVSROOT/.gitignore
new file mode 100644 (file)
index 0000000..3bb9b34
--- /dev/null
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9603/cvsroot/module/a,v b/t/t9603/cvsroot/module/a,v
new file mode 100644 (file)
index 0000000..ba8fd5a
--- /dev/null
@@ -0,0 +1,74 @@
+head   1.2;
+access;
+symbols
+       A:1.2.0.2;
+locks; strict;
+comment        @# @;
+
+
+1.2
+date   2009.02.21.18.11.14;    author tester;  state Exp;
+branches
+       1.2.2.1;
+next   1.1;
+
+1.1
+date   2009.02.21.18.11.43;    author tester;  state Exp;
+branches;
+next   ;
+
+1.2.2.1
+date   2009.03.11.19.03.52;    author tester;  state Exp;
+branches;
+next   1.2.2.2;
+
+1.2.2.2
+date   2009.03.11.19.09.10;    author tester;  state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Rev 2
+@
+text
+@1.2
+@
+
+
+1.2.2.1
+log
+@Rev 4 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.1
+@
+
+
+1.2.2.2
+log
+@Rev 5 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.2
+@
+
+
+1.1
+log
+@Rev 1
+@
+text
+@d1 1
+a1 1
+1.1
+@
diff --git a/t/t9603/cvsroot/module/b,v b/t/t9603/cvsroot/module/b,v
new file mode 100644 (file)
index 0000000..d268855
--- /dev/null
@@ -0,0 +1,90 @@
+head   1.3;
+access;
+symbols
+       A:1.2.0.2;
+locks; strict;
+comment        @# @;
+
+
+1.3
+date   2009.03.11.19.05.08;    author tester;  state Exp;
+branches;
+next   1.2;
+
+1.2
+date   2009.02.21.18.11.43;    author tester;  state Exp;
+branches
+       1.2.2.1;
+next   1.1;
+
+1.1
+date   2009.02.21.18.11.14;    author tester;  state Exp;
+branches;
+next   ;
+
+1.2.2.1
+date   2009.03.11.19.03.52;    author tester;  state Exp;
+branches;
+next   1.2.2.2;
+
+1.2.2.2
+date   2009.03.11.19.09.10;    author tester;  state Exp;
+branches;
+next   ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Rev 4
+@
+text
+@1.3
+@
+
+
+1.2
+log
+@Rev 3
+@
+text
+@d1 1
+a1 1
+1.2
+@
+
+
+1.2.2.1
+log
+@Rev 4 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.1
+@
+
+
+1.2.2.2
+log
+@Rev 5 Branch A
+@
+text
+@d1 1
+a1 1
+1.2
+@
+
+
+1.1
+log
+@Rev 2
+@
+text
+@d1 1
+a1 1
+1.1
+@
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..f2ca5364722e9c85a23bdfdcf1e24122fd5e3a0f 100644 (file)
@@ -114,8 +114,11 @@ do
                valgrind=t; verbose=t; shift ;;
        --tee)
                shift ;; # was handled already
+       --root=*)
+               root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        *)
-               break ;;
+               echo "error: unknown test option '$1'" >&2; exit 1 ;;
        esac
 done
 
@@ -147,7 +150,7 @@ fi
 
 error () {
        say_color error "error: $*"
-       trap - EXIT
+       GIT_EXIT_OK=t
        exit 1
 }
 
@@ -179,10 +182,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 +295,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 +357,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 +508,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}-$$"
@@ -638,9 +648,14 @@ fi
 
 # Test repository
 test="trash directory.$(basename "$0" .sh)"
-test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
+test -n "$root" && test="$root/$test"
+case "$test" in
+/*) TRASH_DIRECTORY="$test" ;;
+ *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+esac
+test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
 rm -fr "$test" || {
-       trap - EXIT
+       GIT_EXIT_OK=t
        echo >&5 "FATAL: Cannot prepare test area"
        exit 1
 }
@@ -670,6 +685,21 @@ do
        esac
 done
 
+# Provide an implementation of the 'yes' utility
+yes () {
+       if test $# = 0
+       then
+               y=y
+       else
+               y="$*"
+       fi
+
+       while echo "$y"
+       do
+               :
+       done
+}
+
 # Fix some commands on Windows
 case $(uname -s) in
 *MINGW*)
index 18d2e0f72768c103d593cc2cf6d2b7a4bc8a9a01..7a83e17ab5f03cdc8284be789e07721d24e8d389 100755 (executable)
@@ -9,7 +9,7 @@
 # For example:
 #  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
 #
-# see contrib/hooks/ for an sample, or uncomment the next line and
+# see contrib/hooks/ for a sample, or uncomment the next line and
 # rename the file to "post-receive".
 
 #. /usr/share/doc/git-core/contrib/hooks/post-receive-email
index 0e49279c7f7b805c78f1bc4760a0d1c70a84a0d9..043970a751f0a85cfa99684be5d610b33b15a358 100755 (executable)
@@ -7,7 +7,35 @@
 #
 # To enable this hook, rename this file to "pre-commit".
 
-if git-rev-parse --verify HEAD 2>/dev/null
+# 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" ] &&
+       # Note that the use of brackets around a tr range is ok here, (it's
+       # even required, for portability to Solaris 10's /usr/bin/tr), since
+       # the square bracket bytes happen to fall in the designated range.
+       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 file name."
+       echo
+       echo "This can cause problems if you want to work"
+       echo "with people on other platforms."
+       echo
+       echo "To be portable it is advisable 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
 else
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 62e8f2387a1cab97ec1c71d1993d082274e17bf5..a9e705f79a148790f4de55c7918dc9c669382a4f 100644 (file)
@@ -1,20 +1,67 @@
 #include "cache.h"
 
-int main(int argc, char **argv)
+static const char *usage_msg = "\n"
+"  test-date show [time_t]...\n"
+"  test-date parse [date]...\n"
+"  test-date approxidate [date]...\n";
+
+static void show_dates(char **argv, struct timeval *now)
 {
-       int i;
+       char buf[128];
+
+       for (; *argv; argv++) {
+               time_t t = atoi(*argv);
+               show_date_relative(t, 0, now, buf, sizeof(buf));
+               printf("%s -> %s\n", *argv, buf);
+       }
+}
 
-       for (i = 1; i < argc; i++) {
+static void parse_dates(char **argv, struct timeval *now)
+{
+       for (; *argv; argv++) {
                char result[100];
                time_t t;
 
-               memcpy(result, "bad", 4);
-               parse_date(argv[i], result, sizeof(result));
+               result[0] = 0;
+               parse_date(*argv, result, sizeof(result));
                t = strtoul(result, NULL, 0);
-               printf("%s -> %s -> %s", argv[i], result, ctime(&t));
+               printf("%s -> %s\n", *argv,
+                       t ? show_date(t, 0, DATE_ISO8601) : "bad");
+       }
+}
 
-               t = approxidate(argv[i]);
-               printf("%s -> %s\n", argv[i], ctime(&t));
+static void parse_approxidate(char **argv, struct timeval *now)
+{
+       for (; *argv; argv++) {
+               time_t t;
+               t = approxidate_relative(*argv, now);
+               printf("%s -> %s\n", *argv, show_date(t, 0, DATE_ISO8601));
+       }
+}
+
+int main(int argc, char **argv)
+{
+       struct timeval now;
+       const char *x;
+
+       x = getenv("TEST_DATE_NOW");
+       if (x) {
+               now.tv_sec = atoi(x);
+               now.tv_usec = 0;
        }
+       else
+               gettimeofday(&now, NULL);
+
+       argv++;
+       if (!*argv)
+               usage(usage_msg);
+       if (!strcmp(*argv, "show"))
+               show_dates(argv+1, &now);
+       else if (!strcmp(*argv, "parse"))
+               parse_dates(argv+1, &now);
+       else if (!strcmp(*argv, "approxidate"))
+               parse_approxidate(argv+1, &now);
+       else
+               usage(usage_msg);
        return 0;
 }
index 3d885ff37ee7fc43dec05dd827679d68cee5516b..af40a3c49ee0df5f0caaebae8e9bc6e9ffdb092b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * test-delta.c: test code to exercise diff-delta.c and patch-delta.c
  *
- * (C) 2005 Nicolas Pitre <nico@cam.org>
+ * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
index 8ad276d062d6b96e2d80d3fcb44e046287f34bf7..b3c28d9a1c1102f2862af68b0bda7e9b0424f5cc 100644 (file)
@@ -4,8 +4,7 @@
  * Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
+#include "git-compat-util.h"
 
 int main(int argc, char *argv[])
 {
index 61d2c39814529bd0264e4c9e40241131d51d819c..acd1a2ba70fc2ffb38cd8fb0784aa6e5e693183f 100644 (file)
@@ -7,8 +7,10 @@ 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;
+static int ambiguous;
 
-int length_callback(const struct option *opt, const char *arg, int unset)
+static int length_callback(const struct option *opt, const char *arg, int unset)
 {
        printf("Callback: \"%s\", %d\n",
                (arg ? arg : "not set"), unset);
@@ -19,8 +21,15 @@ int length_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static 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 +38,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 +46,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 +56,14 @@ 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 },
+               { OPTION_BOOLEAN, 0, "ambiguous", &ambiguous, NULL,
+                 "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+               { OPTION_BOOLEAN, 0, "no-ambiguous", &ambiguous, NULL,
+                 "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
                OPT_GROUP("Standard options"),
                OPT__ABBREV(&abbrev),
                OPT__VERBOSE(&verbose),
@@ -54,7 +73,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 +83,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 9b98d07c786b36f005f4b3ffd51926f3c1a343dd..80daba980ecd85852ee3a23c155602e4cd1ba07a 100644 (file)
@@ -32,7 +32,7 @@ int main(int ac, char **av)
                        if (sz == 0)
                                break;
                        if (sz < 0)
-                               die("test-sha1: %s", strerror(errno));
+                               die_errno("test-sha1");
                        this_sz += sz;
                        cp += sz;
                        room -= sz;
index 55e7e2904eb5f95cedaec2520ddd1d158ee93c7a..4f9c829c2df319e386b6b5d4f5c23818cc21c979 100644 (file)
@@ -1,9 +1,6 @@
 #include "cache.h"
 
-#ifdef _WIN32
-#  define WIN32_LEAN_AND_MEAN
-#  include <windows.h>
-#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+#if defined(hpux) || defined(__hpux) || defined(_hpux)
 #  include <sys/pstat.h>
 #endif
 
diff --git a/transport-helper.c b/transport-helper.c
new file mode 100644 (file)
index 0000000..f57e84c
--- /dev/null
@@ -0,0 +1,168 @@
+#include "cache.h"
+#include "transport.h"
+
+#include "run-command.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+struct helper_data
+{
+       const char *name;
+       struct child_process *helper;
+       unsigned fetch : 1;
+};
+
+static struct child_process *get_helper(struct transport *transport)
+{
+       struct helper_data *data = transport->data;
+       struct strbuf buf = STRBUF_INIT;
+       struct child_process *helper;
+       FILE *file;
+
+       if (data->helper)
+               return data->helper;
+
+       helper = xcalloc(1, sizeof(*helper));
+       helper->in = -1;
+       helper->out = -1;
+       helper->err = 0;
+       helper->argv = xcalloc(4, sizeof(*helper->argv));
+       strbuf_addf(&buf, "remote-%s", data->name);
+       helper->argv[0] = strbuf_detach(&buf, NULL);
+       helper->argv[1] = transport->remote->name;
+       helper->argv[2] = transport->url;
+       helper->git_cmd = 1;
+       if (start_command(helper))
+               die("Unable to run helper: git %s", helper->argv[0]);
+       data->helper = helper;
+
+       write_str_in_full(helper->in, "capabilities\n");
+
+       file = xfdopen(helper->out, "r");
+       while (1) {
+               if (strbuf_getline(&buf, file, '\n') == EOF)
+                       exit(128); /* child died, message supplied already */
+
+               if (!*buf.buf)
+                       break;
+               if (!strcmp(buf.buf, "fetch"))
+                       data->fetch = 1;
+       }
+       return data->helper;
+}
+
+static int disconnect_helper(struct transport *transport)
+{
+       struct helper_data *data = transport->data;
+       if (data->helper) {
+               write_str_in_full(data->helper->in, "\n");
+               close(data->helper->in);
+               finish_command(data->helper);
+               free((char *)data->helper->argv[0]);
+               free(data->helper->argv);
+               free(data->helper);
+               data->helper = NULL;
+       }
+       return 0;
+}
+
+static int fetch_with_fetch(struct transport *transport,
+                           int nr_heads, const struct ref **to_fetch)
+{
+       struct child_process *helper = get_helper(transport);
+       FILE *file = xfdopen(helper->out, "r");
+       int i;
+       struct strbuf buf = STRBUF_INIT;
+
+       for (i = 0; i < nr_heads; i++) {
+               const struct ref *posn = to_fetch[i];
+               if (posn->status & REF_STATUS_UPTODATE)
+                       continue;
+
+               strbuf_addf(&buf, "fetch %s %s\n",
+                           sha1_to_hex(posn->old_sha1), posn->name);
+               write_in_full(helper->in, buf.buf, buf.len);
+               strbuf_reset(&buf);
+
+               if (strbuf_getline(&buf, file, '\n') == EOF)
+                       exit(128); /* child died, message supplied already */
+       }
+       return 0;
+}
+
+static int fetch(struct transport *transport,
+                int nr_heads, const struct ref **to_fetch)
+{
+       struct helper_data *data = transport->data;
+       int i, count;
+
+       count = 0;
+       for (i = 0; i < nr_heads; i++)
+               if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
+                       count++;
+
+       if (!count)
+               return 0;
+
+       if (data->fetch)
+               return fetch_with_fetch(transport, nr_heads, to_fetch);
+
+       return -1;
+}
+
+static struct ref *get_refs_list(struct transport *transport, int for_push)
+{
+       struct child_process *helper;
+       struct ref *ret = NULL;
+       struct ref **tail = &ret;
+       struct ref *posn;
+       struct strbuf buf = STRBUF_INIT;
+       FILE *file;
+
+       helper = get_helper(transport);
+
+       write_str_in_full(helper->in, "list\n");
+
+       file = xfdopen(helper->out, "r");
+       while (1) {
+               char *eov, *eon;
+               if (strbuf_getline(&buf, file, '\n') == EOF)
+                       exit(128); /* child died, message supplied already */
+
+               if (!*buf.buf)
+                       break;
+
+               eov = strchr(buf.buf, ' ');
+               if (!eov)
+                       die("Malformed response in ref list: %s", buf.buf);
+               eon = strchr(eov + 1, ' ');
+               *eov = '\0';
+               if (eon)
+                       *eon = '\0';
+               *tail = alloc_ref(eov + 1);
+               if (buf.buf[0] == '@')
+                       (*tail)->symref = xstrdup(buf.buf + 1);
+               else if (buf.buf[0] != '?')
+                       get_sha1_hex(buf.buf, (*tail)->old_sha1);
+               tail = &((*tail)->next);
+       }
+       strbuf_release(&buf);
+
+       for (posn = ret; posn; posn = posn->next)
+               resolve_remote_symref(posn, ret);
+
+       return ret;
+}
+
+int transport_helper_init(struct transport *transport, const char *name)
+{
+       struct helper_data *data = xcalloc(sizeof(*data), 1);
+       data->name = name;
+
+       transport->data = data;
+       transport->get_refs_list = get_refs_list;
+       transport->fetch = fetch;
+       transport->disconnect = disconnect_helper;
+       return 0;
+}
index 3dfb03c06ed82102f60045d6889db97cf036651e..644a30a0b200b339d291f4cc3d35b2268d12facd 100644 (file)
@@ -1,9 +1,6 @@
 #include "cache.h"
 #include "transport.h"
 #include "run-command.h"
-#ifndef NO_CURL
-#include "http.h"
-#endif
 #include "pkt-line.h"
 #include "fetch-pack.h"
 #include "send-pack.h"
@@ -158,7 +155,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 
        strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
        if (!mkdtemp(temp_dir.buf))
-               die ("Could not make temporary directory");
+               die_errno ("Could not make temporary directory");
        temp_dir_len = temp_dir.len;
 
        strbuf_addstr(&buf, rsync_url(transport->url));
@@ -321,7 +318,7 @@ static int rsync_transport_push(struct transport *transport,
 
        strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
        if (!mkdtemp(temp_dir.buf))
-               die ("Could not make temporary directory");
+               die_errno ("Could not make temporary directory");
        strbuf_addch(&temp_dir, '/');
 
        if (flags & TRANSPORT_PUSH_ALL) {
@@ -352,51 +349,11 @@ static int rsync_transport_push(struct transport *transport,
        return result;
 }
 
-/* Generic functions for using commit walkers */
-
-#ifndef NO_CURL /* http fetch is the only user */
-static int fetch_objs_via_walker(struct transport *transport,
-                                int nr_objs, const struct ref **to_fetch)
-{
-       char *dest = xstrdup(transport->url);
-       struct walker *walker = transport->data;
-       char **objs = xmalloc(nr_objs * sizeof(*objs));
-       int i;
-
-       walker->get_all = 1;
-       walker->get_tree = 1;
-       walker->get_history = 1;
-       walker->get_verbosely = transport->verbose >= 0;
-       walker->get_recover = 0;
-
-       for (i = 0; i < nr_objs; i++)
-               objs[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
-
-       if (walker_fetch(walker, nr_objs, objs, NULL, NULL))
-               die("Fetch failed.");
-
-       for (i = 0; i < nr_objs; i++)
-               free(objs[i]);
-       free(objs);
-       free(dest);
-       return 0;
-}
-#endif /* NO_CURL */
-
-static int disconnect_walker(struct transport *transport)
-{
-       struct walker *walker = transport->data;
-       if (walker)
-               walker_free(walker);
-       return 0;
-}
-
 #ifndef NO_CURL
 static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
 {
        const char **argv;
        int argc;
-       int err;
 
        if (flags & TRANSPORT_PUSH_MIRROR)
                return error("http transport does not support mirror mode");
@@ -416,119 +373,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
        while (refspec_nr--)
                argv[argc++] = *refspec++;
        argv[argc] = NULL;
-       err = run_command_v_opt(argv, RUN_GIT_CMD);
-       switch (err) {
-       case -ERR_RUN_COMMAND_FORK:
-               error("unable to fork for %s", argv[0]);
-       case -ERR_RUN_COMMAND_EXEC:
-               error("unable to exec %s", argv[0]);
-               break;
-       case -ERR_RUN_COMMAND_WAITPID:
-       case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-       case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-       case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-               error("%s died with strange error", argv[0]);
-       }
-       return !!err;
-}
-
-static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
-{
-       struct strbuf buffer = STRBUF_INIT;
-       char *data, *start, *mid;
-       char *ref_name;
-       char *refs_url;
-       int i = 0;
-
-       struct active_request_slot *slot;
-       struct slot_results results;
-
-       struct ref *refs = NULL;
-       struct ref *ref = NULL;
-       struct ref *last_ref = NULL;
-
-       struct walker *walker;
-
-       if (for_push)
-               return NULL;
-
-       if (!transport->data)
-               transport->data = get_http_walker(transport->url,
-                                               transport->remote);
-
-       walker = transport->data;
-
-       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");
-       }
-
-       data = buffer.buf;
-       start = NULL;
-       mid = data;
-       while (i < buffer.len) {
-               if (!start)
-                       start = &data[i];
-               if (data[i] == '\t')
-                       mid = &data[i];
-               if (data[i] == '\n') {
-                       data[i] = 0;
-                       ref_name = mid + 1;
-                       ref = xmalloc(sizeof(struct ref) +
-                                     strlen(ref_name) + 1);
-                       memset(ref, 0, sizeof(struct ref));
-                       strcpy(ref->name, ref_name);
-                       get_sha1_hex(start, ref->old_sha1);
-                       if (!refs)
-                               refs = ref;
-                       if (last_ref)
-                               last_ref->next = ref;
-                       last_ref = ref;
-                       start = NULL;
-               }
-               i++;
-       }
-
-       strbuf_release(&buffer);
-
-       ref = alloc_ref("HEAD");
-       if (!walker->fetch_ref(walker, ref) &&
-           !resolve_remote_symref(ref, refs)) {
-               ref->next = refs;
-               refs = ref;
-       } else {
-               free(ref);
-       }
-
-       return refs;
-}
-
-static int fetch_objs_via_curl(struct transport *transport,
-                                int nr_objs, const struct ref **to_fetch)
-{
-       if (!transport->data)
-               transport->data = get_http_walker(transport->url,
-                                               transport->remote);
-       return fetch_objs_via_walker(transport, nr_objs, to_fetch);
+       return !!run_command_v_opt(argv, RUN_GIT_CMD);
 }
 
 #endif
@@ -690,6 +535,21 @@ static int fetch_refs_via_pack(struct transport *transport,
        return (refs ? 0 : -1);
 }
 
+static int push_had_errors(struct ref *ref)
+{
+       for (; ref; ref = ref->next) {
+               switch (ref->status) {
+               case REF_STATUS_NONE:
+               case REF_STATUS_UPTODATE:
+               case REF_STATUS_OK:
+                       break;
+               default:
+                       return 1;
+               }
+       }
+       return 0;
+}
+
 static int refs_pushed(struct ref *ref)
 {
        for (; ref; ref = ref->next) {
@@ -728,19 +588,30 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref, int verb
 
 #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
-static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg, int porcelain)
 {
-       fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
-       if (from)
-               fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
-       else
-               fputs(prettify_ref(to), stderr);
-       if (msg) {
-               fputs(" (", stderr);
-               fputs(msg, stderr);
-               fputc(')', stderr);
+       if (porcelain) {
+               if (from)
+                       fprintf(stdout, "%c\t%s:%s\t", flag, from->name, to->name);
+               else
+                       fprintf(stdout, "%c\t:%s\t", flag, to->name);
+               if (msg)
+                       fprintf(stdout, "%s (%s)\n", summary, msg);
+               else
+                       fprintf(stdout, "%s\n", summary);
+       } else {
+               fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+               if (from)
+                       fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
+               else
+                       fputs(prettify_refname(to->name), stderr);
+               if (msg) {
+                       fputs(" (", stderr);
+                       fputs(msg, stderr);
+                       fputc(')', stderr);
+               }
+               fputc('\n', stderr);
        }
-       fputc('\n', stderr);
 }
 
 static const char *status_abbrev(unsigned char sha1[20])
@@ -748,15 +619,15 @@ static const char *status_abbrev(unsigned char sha1[20])
        return find_unique_abbrev(sha1, DEFAULT_ABBREV);
 }
 
-static void print_ok_ref_status(struct ref *ref)
+static void print_ok_ref_status(struct ref *ref, int porcelain)
 {
        if (ref->deletion)
-               print_ref_status('-', "[deleted]", ref, NULL, NULL);
+               print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain);
        else if (is_null_sha1(ref->old_sha1))
                print_ref_status('*',
                        (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
-                         "[new branch]"),
-                       ref, ref->peer_ref, NULL);
+                       "[new branch]"),
+                       ref, ref->peer_ref, NULL, porcelain);
        else {
                char quickref[84];
                char type;
@@ -774,50 +645,51 @@ static void print_ok_ref_status(struct ref *ref)
                }
                strcat(quickref, status_abbrev(ref->new_sha1));
 
-               print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+               print_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);
        }
 }
 
-static int print_one_push_status(struct ref *ref, const char *dest, int count)
+static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)
 {
        if (!count)
                fprintf(stderr, "To %s\n", dest);
 
        switch(ref->status) {
        case REF_STATUS_NONE:
-               print_ref_status('X', "[no match]", ref, NULL, NULL);
+               print_ref_status('X', "[no match]", ref, NULL, NULL, porcelain);
                break;
        case REF_STATUS_REJECT_NODELETE:
                print_ref_status('!', "[rejected]", ref, NULL,
-                               "remote does not support deleting refs");
+                                                "remote does not support deleting refs", porcelain);
                break;
        case REF_STATUS_UPTODATE:
                print_ref_status('=', "[up to date]", ref,
-                               ref->peer_ref, NULL);
+                                                ref->peer_ref, NULL, porcelain);
                break;
        case REF_STATUS_REJECT_NONFASTFORWARD:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                               "non-fast forward");
+                                                "non-fast forward", porcelain);
                break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
-                               ref->deletion ? NULL : ref->peer_ref,
-                               ref->remote_status);
+                                                ref->deletion ? NULL : ref->peer_ref,
+                                                ref->remote_status, porcelain);
                break;
        case REF_STATUS_EXPECTING_REPORT:
                print_ref_status('!', "[remote failure]", ref,
-                               ref->deletion ? NULL : ref->peer_ref,
-                               "remote failed to report status");
+                                                ref->deletion ? NULL : ref->peer_ref,
+                                                "remote failed to report status", porcelain);
                break;
        case REF_STATUS_OK:
-               print_ok_ref_status(ref);
+               print_ok_ref_status(ref, porcelain);
                break;
        }
 
        return 1;
 }
 
-static void print_push_status(const char *dest, struct ref *refs, int verbose)
+static void print_push_status(const char *dest, struct ref *refs,
+                             int verbose, int porcelain, int * nonfastforward)
 {
        struct ref *ref;
        int n = 0;
@@ -825,18 +697,21 @@ static void print_push_status(const char *dest, struct ref *refs, int verbose)
        if (verbose) {
                for (ref = refs; ref; ref = ref->next)
                        if (ref->status == REF_STATUS_UPTODATE)
-                               n += print_one_push_status(ref, dest, n);
+                               n += print_one_push_status(ref, dest, n, porcelain);
        }
 
        for (ref = refs; ref; ref = ref->next)
                if (ref->status == REF_STATUS_OK)
-                       n += print_one_push_status(ref, dest, n);
+                       n += print_one_push_status(ref, dest, n, porcelain);
 
+       *nonfastforward = 0;
        for (ref = refs; ref; ref = ref->next) {
                if (ref->status != REF_STATUS_NONE &&
                    ref->status != REF_STATUS_UPTODATE &&
                    ref->status != REF_STATUS_OK)
-                       n += print_one_push_status(ref, dest, n);
+                       n += print_one_push_status(ref, dest, n, porcelain);
+               if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD)
+                       *nonfastforward = 1;
        }
 }
 
@@ -889,6 +764,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
        args.use_thin_pack = data->thin;
        args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
+       args.quiet = !!(flags & TRANSPORT_PUSH_QUIET);
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
 
        ret = send_pack(&args, data->fd, data->conn, remote_refs,
@@ -947,14 +823,12 @@ struct transport *transport_get(struct remote *remote, const char *url)
        } else if (!prefixcmp(url, "http://")
                || !prefixcmp(url, "https://")
                || !prefixcmp(url, "ftp://")) {
+               transport_helper_init(ret, "curl");
 #ifdef NO_CURL
                error("git was compiled without libcurl support.");
 #else
-               ret->get_refs_list = get_refs_via_curl;
-               ret->fetch = fetch_objs_via_curl;
                ret->push = curl_transport_push;
 #endif
-               ret->disconnect = disconnect_walker;
 
        } else if (is_local(url) && is_file(url)) {
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
@@ -994,8 +868,10 @@ int transport_set_option(struct transport *transport,
 }
 
 int transport_push(struct transport *transport,
-                  int refspec_nr, const char **refspec, int flags)
+                  int refspec_nr, const char **refspec, int flags,
+                  int *nonfastforward)
 {
+       *nonfastforward = 0;
        verify_remote_names(refspec_nr, refspec);
 
        if (transport->push)
@@ -1003,10 +879,11 @@ 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;
+               int quiet = flags & TRANSPORT_PUSH_QUIET;
+               int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
                int ret;
 
                if (flags & TRANSPORT_PUSH_ALL)
@@ -1014,17 +891,17 @@ 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;
                }
 
                ret = transport->push_refs(transport, remote_refs, flags);
 
-               print_push_status(transport->url, remote_refs, verbose);
+               if (!quiet || push_had_errors(remote_refs))
+                       print_push_status(transport->url, remote_refs,
+                                       verbose | porcelain, porcelain,
+                                       nonfastforward);
 
                if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
                        struct ref *ref;
@@ -1032,7 +909,7 @@ int transport_push(struct transport *transport,
                                update_tracking_ref(transport->remote, ref, verbose);
                }
 
-               if (!ret && !refs_pushed(remote_refs))
+               if (!quiet && !ret && !refs_pushed(remote_refs))
                        fprintf(stderr, "Everything up-to-date\n");
                return ret;
        }
@@ -1049,11 +926,12 @@ const struct ref *transport_get_remote_refs(struct transport *transport)
 int transport_fetch_refs(struct transport *transport, const struct ref *refs)
 {
        int rc;
-       int nr_heads = 0, nr_alloc = 0;
+       int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
        const struct ref **heads = NULL;
        const struct ref *rm;
 
        for (rm = refs; rm; rm = rm->next) {
+               nr_refs++;
                if (rm->peer_ref &&
                    !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
                        continue;
@@ -1061,6 +939,19 @@ int transport_fetch_refs(struct transport *transport, const struct ref *refs)
                heads[nr_heads++] = rm;
        }
 
+       if (!nr_heads) {
+               /*
+                * When deepening of a shallow repository is requested,
+                * then local and remote refs are likely to still be equal.
+                * Just feed them all to the fetch method in that case.
+                * This condition shouldn't be met in a non-deepening fetch
+                * (see builtin-fetch.c:quickfetch()).
+                */
+               heads = xmalloc(nr_refs * sizeof(*heads));
+               for (rm = refs; rm; rm = rm->next)
+                       heads[nr_heads++] = rm;
+       }
+
        rc = transport->fetch(transport, nr_heads, heads);
        free(heads);
        return rc;
@@ -1069,7 +960,7 @@ int transport_fetch_refs(struct transport *transport, const struct ref *refs)
 void transport_unlock_pack(struct transport *transport)
 {
        if (transport->pack_lockfile) {
-               unlink(transport->pack_lockfile);
+               unlink_or_warn(transport->pack_lockfile);
                free(transport->pack_lockfile);
                transport->pack_lockfile = NULL;
        }
@@ -1083,3 +974,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..c14da6f1e53ece9fa9ac2e68b73674c54329be5f 100644 (file)
@@ -35,6 +35,8 @@ struct transport {
 #define TRANSPORT_PUSH_DRY_RUN 4
 #define TRANSPORT_PUSH_MIRROR 8
 #define TRANSPORT_PUSH_VERBOSE 16
+#define TRANSPORT_PUSH_PORCELAIN 32
+#define TRANSPORT_PUSH_QUIET 64
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
@@ -67,12 +69,17 @@ int transport_set_option(struct transport *transport, const char *name,
                         const char *value);
 
 int transport_push(struct transport *connection,
-                  int refspec_nr, const char **refspec, int flags);
+                  int refspec_nr, const char **refspec, int flags,
+                  int * nonfastforward);
 
 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);
+
+/* Transport methods defined outside transport.c */
+int transport_helper_init(struct transport *transport, const char *name);
 
 #endif
index edd83949bf0896bedff4cab8e2b037eba574a45e..0459e54d3d89a413330f52ab34662f51924b04ea 100644 (file)
@@ -239,6 +239,12 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
                if (!tree || type != OBJ_TREE)
                        die("corrupt tree sha %s", sha1_to_hex(sha1));
 
+               if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
+                       newbase[baselen + pathlen] = 0;
+                       opt->add_remove(opt, *prefix, mode, sha1, newbase);
+                       newbase[baselen + pathlen] = '/';
+               }
+
                init_tree_desc(&inner, tree, size);
                show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
 
index 75cd2f1a6adf3ea773a6acc3f1e7f122e30676e5..ac9cbf7cd8ed1367151de0e8b96668f498b7f1a1 100644 (file)
@@ -17,7 +17,7 @@ static char *create_temp_file(unsigned char *sha1)
        strcpy(path, ".merge_file_XXXXXX");
        fd = xmkstemp(path);
        if (write_in_full(fd, buf, size) != size)
-               die("unable to write temp-file");
+               die_errno("unable to write temp-file");
        close(fd);
        return path;
 }
index e4eb8fa3afc7ad895ba925701ee3c6881b29bd91..720f7a161651126872b7357173c10d69d1db7636 100644 (file)
@@ -61,7 +61,7 @@ static void unlink_entry(struct cache_entry *ce)
 {
        if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
                return;
-       if (unlink(ce->name))
+       if (unlink_or_warn(ce->name))
                return;
        schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
@@ -128,7 +128,7 @@ static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_o
 
 static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-       struct cache_entry *src[5] = { ce, };
+       struct cache_entry *src[5] = { ce, NULL, };
 
        o->pos++;
        if (ce_stage(ce)) {
@@ -140,7 +140,7 @@ static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_option
        return call_unpack_fn(src, o);
 }
 
-int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
+static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
 {
        int i;
        struct tree_desc t[MAX_UNPACK_TREES];
@@ -326,6 +326,23 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
                        if (src[0])
                                conflicts |= 1;
                }
+
+               /* special case: "diff-index --cached" looking at a tree */
+               if (o->diff_index_cached &&
+                   n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {
+                       int matches;
+                       matches = cache_tree_matches_traversal(o->src_index->cache_tree,
+                                                              names, info);
+                       /*
+                        * Everything under the name matches.  Adjust o->pos to
+                        * skip the entire hierarchy.
+                        */
+                       if (matches) {
+                               o->pos += matches;
+                               return mask;
+                       }
+               }
+
                if (traverse_trees_recursive(n, dirmask, conflicts,
                                             names, info) < 0)
                        return -1;
@@ -534,7 +551,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
        memset(&d, 0, sizeof(d));
        if (o->dir)
                d.exclude_per_dir = o->dir->exclude_per_dir;
-       i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
+       i = read_directory(&d, pathbuf, namelen+1, NULL);
        if (i)
                return o->gently ? -1 :
                        error(ERRORMSG(o, not_uptodate_dir), ce->name);
@@ -982,12 +999,12 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
                return error("Cannot do a oneway merge of %d trees",
                             o->merge_size);
 
-       if (!a)
+       if (!a || a == o->df_conflict_entry)
                return deleted_entry(old, old, o);
 
        if (old && same(old, a)) {
                int update = 0;
-               if (o->reset) {
+               if (o->reset && !ce_uptodate(old)) {
                        struct stat st;
                        if (lstat(old->name, &st) ||
                            ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))
index 0d26f3d73e773230972db86f34a5147ada881e8b..d19df44f4070ca1bda29382e56fbced53aacd994 100644 (file)
@@ -17,17 +17,18 @@ struct unpack_trees_error_msgs {
 };
 
 struct unpack_trees_options {
-       unsigned int reset:1,
-                    merge:1,
-                    update:1,
-                    index_only:1,
-                    nontrivial_merge:1,
-                    trivial_merges_only:1,
-                    verbose_update:1,
-                    aggressive:1,
-                    skip_unmerged:1,
-                    initial_checkout:1,
-                    gently:1;
+       unsigned int reset,
+                    merge,
+                    update,
+                    index_only,
+                    nontrivial_merge,
+                    trivial_merges_only,
+                    verbose_update,
+                    aggressive,
+                    skip_unmerged,
+                    initial_checkout,
+                    diff_index_cached,
+                    gently;
        const char *prefix;
        int pos;
        struct dir_struct *dir;
diff --git a/update-server-info.c b/update-server-info.c
deleted file mode 100644 (file)
index 7b38fd8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "cache.h"
-#include "exec_cmd.h"
-
-static const char update_server_info_usage[] =
-"git update-server-info [--force]";
-
-int main(int ac, char **av)
-{
-       int i;
-       int force = 0;
-       for (i = 1; i < ac; i++) {
-               if (av[i][0] == '-') {
-                       if (!strcmp("--force", av[i]) ||
-                           !strcmp("-f", av[i]))
-                               force = 1;
-                       else
-                               usage(update_server_info_usage);
-               }
-       }
-       if (i != ac)
-               usage(update_server_info_usage);
-
-       git_extract_argv0_path(av[0]);
-
-       setup_git_directory();
-
-       return !!update_server_info(force);
-}
index edc78612289c5bd774e18fe5f8e1b9ea80378a5f..38ddac2e8607f4cc6a801eeb49ba755dd89ee81c 100644 (file)
@@ -28,9 +28,11 @@ static unsigned long oldest_have;
 
 static int multi_ack, nr_our_refs;
 static int use_thin_pack, use_ofs_delta, use_include_tag;
-static int no_progress;
+static int no_progress, daemon_mode;
+static int shallow_nr;
 static struct object_array have_obj;
 static struct object_array want_obj;
+static struct object_array extra_edge_obj;
 static unsigned int timeout;
 /* 0 for no sideband,
  * otherwise maximum packet size (up to 65520 bytes).
@@ -106,9 +108,7 @@ static int do_rev_list(int fd, void *create_full_pack)
        int i;
        struct rev_info revs;
 
-       pack_pipe = fdopen(fd, "w");
-       if (create_full_pack)
-               use_thin_pack = 0; /* no point doing it */
+       pack_pipe = xfdopen(fd, "w");
        init_revisions(&revs, NULL);
        revs.tag_objects = 1;
        revs.tree_objects = 1;
@@ -136,14 +136,76 @@ static int do_rev_list(int fd, void *create_full_pack)
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
+       if (use_thin_pack)
+               for (i = 0; i < extra_edge_obj.nr; i++)
+                       fprintf(pack_pipe, "-%s\n", sha1_to_hex(
+                                       extra_edge_obj.objects[i].item->sha1));
        traverse_commit_list(&revs, show_commit, show_object, NULL);
        fflush(pack_pipe);
        fclose(pack_pipe);
        return 0;
 }
 
+static int feed_msg_to_hook(int fd, const char *fmt, ...)
+{
+       int cnt;
+       char buf[1024];
+       va_list params;
+
+       va_start(params, fmt);
+       cnt = vsprintf(buf, fmt, params);
+       va_end(params);
+       return write_in_full(fd, buf, cnt) != cnt;
+}
+
+static int feed_obj_to_hook(const char *label, struct object_array *oa, int i, int fd)
+{
+       return feed_msg_to_hook(fd, "%s %s\n", label,
+                               sha1_to_hex(oa->objects[i].item->sha1));
+}
+
+static int run_post_upload_pack_hook(size_t total, struct timeval *tv)
+{
+       const char *argv[2];
+       struct child_process proc;
+       int err, i;
+
+       argv[0] = "hooks/post-upload-pack";
+       argv[1] = NULL;
+
+       if (access(argv[0], X_OK) < 0)
+               return 0;
+
+       memset(&proc, 0, sizeof(proc));
+       proc.argv = argv;
+       proc.in = -1;
+       proc.stdout_to_stderr = 1;
+       err = start_command(&proc);
+       if (err)
+               return err;
+       for (i = 0; !err && i < want_obj.nr; i++)
+               err |= feed_obj_to_hook("want", &want_obj, i, proc.in);
+       for (i = 0; !err && i < have_obj.nr; i++)
+               err |= feed_obj_to_hook("have", &have_obj, i, proc.in);
+       if (!err)
+               err |= feed_msg_to_hook(proc.in, "time %ld.%06ld\n",
+                                       (long)tv->tv_sec, (long)tv->tv_usec);
+       if (!err)
+               err |= feed_msg_to_hook(proc.in, "size %ld\n", (long)total);
+       if (!err)
+               err |= feed_msg_to_hook(proc.in, "kind %s\n",
+                                       (nr_our_refs == want_obj.nr && !have_obj.nr)
+                                       ? "clone" : "fetch");
+       if (close(proc.in))
+               err = 1;
+       if (finish_command(&proc))
+               err = 1;
+       return err;
+}
+
 static void create_pack_file(void)
 {
+       struct timeval start_tv, tv;
        struct async rev_list;
        struct child_process pack_objects;
        int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr);
@@ -151,17 +213,27 @@ static void create_pack_file(void)
        char abort_msg[] = "aborting due to possible repository "
                "corruption on the remote side.";
        int buffered = -1;
-       ssize_t sz;
+       ssize_t sz, total_sz;
        const char *argv[10];
        int arg = 0;
 
-       rev_list.proc = do_rev_list;
-       /* .data is just a boolean: any non-NULL value will do */
-       rev_list.data = create_full_pack ? &rev_list : NULL;
-       if (start_async(&rev_list))
-               die("git upload-pack: unable to fork git-rev-list");
+       gettimeofday(&start_tv, NULL);
+       total_sz = 0;
+       if (shallow_nr) {
+               rev_list.proc = do_rev_list;
+               rev_list.data = 0;
+               if (start_async(&rev_list))
+                       die("git upload-pack: unable to fork git-rev-list");
+               argv[arg++] = "pack-objects";
+       } else {
+               argv[arg++] = "pack-objects";
+               argv[arg++] = "--revs";
+               if (create_full_pack)
+                       argv[arg++] = "--all";
+               else if (use_thin_pack)
+                       argv[arg++] = "--thin";
+       }
 
-       argv[arg++] = "pack-objects";
        argv[arg++] = "--stdout";
        if (!no_progress)
                argv[arg++] = "--progress";
@@ -172,7 +244,7 @@ static void create_pack_file(void)
        argv[arg++] = NULL;
 
        memset(&pack_objects, 0, sizeof(pack_objects));
-       pack_objects.in = rev_list.out; /* start_command closes it */
+       pack_objects.in = shallow_nr ? rev_list.out : -1;
        pack_objects.out = -1;
        pack_objects.err = -1;
        pack_objects.git_cmd = 1;
@@ -181,6 +253,24 @@ static void create_pack_file(void)
        if (start_command(&pack_objects))
                die("git upload-pack: unable to fork git-pack-objects");
 
+       /* pass on revisions we (don't) want */
+       if (!shallow_nr) {
+               FILE *pipe_fd = xfdopen(pack_objects.in, "w");
+               if (!create_full_pack) {
+                       int i;
+                       for (i = 0; i < want_obj.nr; i++)
+                               fprintf(pipe_fd, "%s\n", sha1_to_hex(want_obj.objects[i].item->sha1));
+                       fprintf(pipe_fd, "--not\n");
+                       for (i = 0; i < have_obj.nr; i++)
+                               fprintf(pipe_fd, "%s\n", sha1_to_hex(have_obj.objects[i].item->sha1));
+               }
+
+               fprintf(pipe_fd, "\n");
+               fflush(pipe_fd);
+               fclose(pipe_fd);
+       }
+
+
        /* We read from pack_objects.err to capture stderr output for
         * progress bar, and pack_objects.out to capture the pack data.
         */
@@ -237,7 +327,7 @@ static void create_pack_file(void)
                        sz = xread(pack_objects.out, cp,
                                  sizeof(data) - outsz);
                        if (0 < sz)
-                                       ;
+                               total_sz += sz;
                        else if (sz == 0) {
                                close(pack_objects.out);
                                pack_objects.out = -1;
@@ -276,7 +366,7 @@ static void create_pack_file(void)
                error("git upload-pack: git-pack-objects died with error.");
                goto fail;
        }
-       if (finish_async(&rev_list))
+       if (shallow_nr && finish_async(&rev_list))
                goto fail;      /* error was already reported */
 
        /* flush the data */
@@ -289,6 +379,16 @@ static void create_pack_file(void)
        }
        if (use_sideband)
                packet_flush(1);
+
+       gettimeofday(&tv, NULL);
+       tv.tv_sec -= start_tv.tv_sec;
+       if (tv.tv_usec < start_tv.tv_usec) {
+               tv.tv_sec--;
+               tv.tv_usec += 1000000;
+       }
+       tv.tv_usec -= start_tv.tv_usec;
+       if (run_post_upload_pack_hook(total_sz, &tv))
+               warning("post-upload-hook failed");
        return;
 
  fail:
@@ -402,7 +502,7 @@ static int get_common_commits(void)
 
        save_commit_buffer = 0;
 
-       for(;;) {
+       for (;;) {
                int len = packet_read_line(0, line, sizeof(line));
                reset_timeout();
 
@@ -451,8 +551,9 @@ static void receive_needs(void)
        static char line[1000];
        int len, depth = 0;
 
+       shallow_nr = 0;
        if (debug_fd)
-               write_in_full(debug_fd, "#S\n", 3);
+               write_str_in_full(debug_fd, "#S\n");
        for (;;) {
                struct object *o;
                unsigned char sha1_buf[20];
@@ -466,7 +567,6 @@ static void receive_needs(void)
                if (!prefixcmp(line, "shallow ")) {
                        unsigned char sha1[20];
                        struct object *object;
-                       use_thin_pack = 0;
                        if (get_sha1(line + 8, sha1))
                                die("invalid shallow line: %s", line);
                        object = parse_object(sha1);
@@ -478,7 +578,6 @@ static void receive_needs(void)
                }
                if (!prefixcmp(line, "deepen ")) {
                        char *end;
-                       use_thin_pack = 0;
                        depth = strtol(line + 7, &end, 0);
                        if (end == line + 7 || depth <= 0)
                                die("Invalid deepen: %s", line);
@@ -520,7 +619,11 @@ static void receive_needs(void)
                }
        }
        if (debug_fd)
-               write_in_full(debug_fd, "#E\n", 3);
+               write_str_in_full(debug_fd, "#E\n");
+
+       if (!use_sideband && daemon_mode)
+               no_progress = 1;
+
        if (depth == 0 && shallows.nr == 0)
                return;
        if (depth > 0) {
@@ -534,6 +637,7 @@ static void receive_needs(void)
                                packet_write(1, "shallow %s",
                                                sha1_to_hex(object->sha1));
                                register_shallow(object->sha1);
+                               shallow_nr++;
                        }
                        result = result->next;
                }
@@ -556,6 +660,7 @@ static void receive_needs(void)
                                                        NULL, &want_obj);
                                        parents = parents->next;
                                }
+                               add_object_array(object, NULL, &extra_edge_obj);
                        }
                        /* make sure commit traversal conforms to client */
                        register_shallow(object->sha1);
@@ -567,6 +672,8 @@ static void receive_needs(void)
                        for (i = 0; i < shallows.nr; i++)
                                register_shallow(shallows.objects[i].item->sha1);
                }
+
+       shallow_nr += shallows.nr;
        free(shallows.objects);
 }
 
@@ -618,6 +725,7 @@ int main(int argc, char **argv)
        int strict = 0;
 
        git_extract_argv0_path(argv[0]);
+       read_replace_refs = 0;
 
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
@@ -630,6 +738,7 @@ int main(int argc, char **argv)
                }
                if (!prefixcmp(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
+                       daemon_mode = 1;
                        continue;
                }
                if (!strcmp(arg, "--")) {
diff --git a/usage.c b/usage.c
index 820d09f92b03b6cc96fbe9a954c37fd2d5e5a417..c488f3afcd6d612e786c13c86241b2a01eb97a2a 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -36,12 +36,12 @@ static void warn_builtin(const char *warn, va_list params)
 
 /* If we are in a dlopen()ed .so write to a global variable would segfault
  * (ugh), so keep things static. */
-static void (*usage_routine)(const char *err) NORETURN = usage_builtin;
-static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin;
+static NORETURN_PTR void (*usage_routine)(const char *err) = usage_builtin;
+static NORETURN_PTR void (*die_routine)(const char *err, va_list params) = die_builtin;
 static void (*error_routine)(const char *err, va_list params) = error_builtin;
 static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
 
-void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN)
+void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params))
 {
        die_routine = routine;
 }
@@ -60,6 +60,34 @@ void die(const char *err, ...)
        va_end(params);
 }
 
+void die_errno(const char *fmt, ...)
+{
+       va_list params;
+       char fmt_with_err[1024];
+       char str_error[256], *err;
+       int i, j;
+
+       err = strerror(errno);
+       for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
+               if ((str_error[j++] = err[i++]) != '%')
+                       continue;
+               if (j < sizeof(str_error) - 1) {
+                       str_error[j++] = '%';
+               } else {
+                       /* No room to double the '%', so we overwrite it with
+                        * '\0' below */
+                       j--;
+                       break;
+               }
+       }
+       str_error[j] = 0;
+       snprintf(fmt_with_err, sizeof(fmt_with_err), "%s: %s", fmt, str_error);
+
+       va_start(params, fmt);
+       die_routine(fmt_with_err, params);
+       va_end(params);
+}
+
 int error(const char *err, ...)
 {
        va_list params;
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;
diff --git a/var.c b/var.c
index 7362ed87354a4bea98c28f9a8c315b7209c67fa2..125c0d1676abae6de29de14061d1a24fd842712d 100644 (file)
--- a/var.c
+++ b/var.c
@@ -21,9 +21,8 @@ static struct git_var git_vars[] = {
 static void list_vars(void)
 {
        struct git_var *ptr;
-       for(ptr = git_vars; ptr->read; ptr++) {
+       for (ptr = git_vars; ptr->read; ptr++)
                printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME));
-       }
 }
 
 static const char *read_var(const char *var)
@@ -31,7 +30,7 @@ static const char *read_var(const char *var)
        struct git_var *ptr;
        const char *val;
        val = NULL;
-       for(ptr = git_vars; ptr->read; ptr++) {
+       for (ptr = git_vars; ptr->read; ptr++) {
                if (strcmp(var, ptr->name) == 0) {
                        val = ptr->read(IDENT_ERROR_ON_NO_NAME);
                        break;
index e57630e983488e5c0222dc47a5e32d6efb184762..11d9052ed86ab17b56ab86ef21b7c98095e410dc 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -245,7 +245,7 @@ void walker_targets_free(int targets, char **target, const char **write_ref)
 {
        while (targets--) {
                free(target[targets]);
-               if (write_ref && write_ref[targets])
+               if (write_ref)
                        free((char *) write_ref[targets]);
        }
 }
index d8efb1365a01104db568633fa8f6aef0c67b4cd1..c9be1400c005e25b003acecc0cb037dd2f07e56f 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -96,7 +96,7 @@ void *xmmap(void *start, size_t length,
                release_pack_memory(length, fd);
                ret = mmap(start, length, prot, flags, fd, offset);
                if (ret == MAP_FAILED)
-                       die("Out of memory? mmap failed: %s", strerror(errno));
+                       die_errno("Out of memory? mmap failed");
        }
        return ret;
 }
@@ -175,7 +175,7 @@ int xdup(int fd)
 {
        int ret = dup(fd);
        if (ret < 0)
-               die("dup failed: %s", strerror(errno));
+               die_errno("dup failed");
        return ret;
 }
 
@@ -183,7 +183,7 @@ FILE *xfdopen(int fd, const char *mode)
 {
        FILE *stream = fdopen(fd, mode);
        if (stream == NULL)
-               die("Out of memory? fdopen failed: %s", strerror(errno));
+               die_errno("Out of memory? fdopen failed");
        return stream;
 }
 
@@ -193,7 +193,7 @@ int xmkstemp(char *template)
 
        fd = mkstemp(template);
        if (fd < 0)
-               die("Unable to create temporary file: %s", strerror(errno));
+               die_errno("Unable to create temporary file");
        return fd;
 }
 
@@ -289,3 +289,19 @@ int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
        safe_create_leading_directories(name);
        return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
 }
+
+int unlink_or_warn(const char *file)
+{
+       int rc = unlink(file);
+
+       if (rc < 0) {
+               int err = errno;
+               if (ENOENT != err) {
+                       warning("unable to unlink %s: %s",
+                               file, strerror(errno));
+                       errno = err;
+               }
+       }
+       return rc;
+}
+
index 4c29255df1b637f93ab3d59e0dcab1fa3b40e10b..d45b536021e15c5a674f7969be39f238194bef99 100644 (file)
@@ -41,14 +41,14 @@ void maybe_flush_or_die(FILE *f, const char *desc)
                 */
                if (errno == EPIPE || errno == EINVAL)
                        exit(0);
-               die("write failure on %s: %s", desc, strerror(errno));
+               die_errno("write failure on '%s'", desc);
        }
 }
 
 void fsync_or_die(int fd, const char *msg)
 {
        if (fsync(fd) < 0) {
-               die("%s: fsync error (%s)", msg, strerror(errno));
+               die_errno("fsync error on '%s'", msg);
        }
 }
 
@@ -57,7 +57,7 @@ void write_or_die(int fd, const void *buf, size_t count)
        if (write_in_full(fd, buf, count) < 0) {
                if (errno == EPIPE)
                        exit(0);
-               die("write error (%s)", strerror(errno));
+               die_errno("write error");
        }
 }
 
diff --git a/ws.c b/ws.c
index b1efcd9d753a29d295702b36fb1beba58fb16995..760b5743fa11f25dd4facf8beeb02e7aa28d09e1 100644 (file)
--- a/ws.c
+++ b/ws.c
 static struct whitespace_rule {
        const char *rule_name;
        unsigned rule_bits;
+       unsigned loosens_error;
 } whitespace_rule_names[] = {
-       { "trailing-space", WS_TRAILING_SPACE },
-       { "space-before-tab", WS_SPACE_BEFORE_TAB },
-       { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB },
-       { "cr-at-eol", WS_CR_AT_EOL },
+       { "trailing-space", WS_TRAILING_SPACE, 0 },
+       { "space-before-tab", WS_SPACE_BEFORE_TAB, 0 },
+       { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB, 0 },
+       { "cr-at-eol", WS_CR_AT_EOL, 1 },
+       { "blank-at-eol", WS_BLANK_AT_EOL, 0 },
+       { "blank-at-eof", WS_BLANK_AT_EOF, 0 },
 };
 
 unsigned parse_whitespace_rule(const char *string)
@@ -79,7 +82,8 @@ unsigned whitespace_rule(const char *pathname)
                        unsigned all_rule = 0;
                        int i;
                        for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
-                               all_rule |= whitespace_rule_names[i].rule_bits;
+                               if (!whitespace_rule_names[i].loosens_error)
+                                       all_rule |= whitespace_rule_names[i].rule_bits;
                        return all_rule;
                } else if (ATTR_FALSE(value)) {
                        /* false (-whitespace) */
@@ -100,8 +104,17 @@ unsigned whitespace_rule(const char *pathname)
 char *whitespace_error_string(unsigned ws)
 {
        struct strbuf err = STRBUF_INIT;
-       if (ws & WS_TRAILING_SPACE)
+       if ((ws & WS_TRAILING_SPACE) == WS_TRAILING_SPACE)
                strbuf_addstr(&err, "trailing whitespace");
+       else {
+               if (ws & WS_BLANK_AT_EOL)
+                       strbuf_addstr(&err, "trailing whitespace");
+               if (ws & WS_BLANK_AT_EOF) {
+                       if (err.len)
+                               strbuf_addstr(&err, ", ");
+                       strbuf_addstr(&err, "new blank line at EOF");
+               }
+       }
        if (ws & WS_SPACE_BEFORE_TAB) {
                if (err.len)
                        strbuf_addstr(&err, ", ");
@@ -139,11 +152,11 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
        }
 
        /* Check for trailing whitespace. */
-       if (ws_rule & WS_TRAILING_SPACE) {
+       if (ws_rule & WS_BLANK_AT_EOL) {
                for (i = len - 1; i >= 0; i--) {
                        if (isspace(line[i])) {
                                trailing_whitespace = i;
-                               result |= WS_TRAILING_SPACE;
+                               result |= WS_BLANK_AT_EOL;
                        }
                        else
                                break;
@@ -259,12 +272,11 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
        /*
         * Strip trailing whitespace
         */
-       if ((ws_rule & WS_TRAILING_SPACE) &&
-           (2 <= len && isspace(src[len-2]))) {
-               if (src[len - 1] == '\n') {
+       if (ws_rule & WS_BLANK_AT_EOL) {
+               if (0 < len && src[len - 1] == '\n') {
                        add_nl_to_tail = 1;
                        len--;
-                       if (1 < len && src[len - 1] == '\r') {
+                       if (0 < len && src[len - 1] == '\r') {
                                add_cr_to_tail = !!(ws_rule & WS_CR_AT_EOL);
                                len--;
                        }
index 1b6df45450c39c0f456df68485b5735950816ac8..38eb24536b34e40af4c43eb1631e08624e8993d6 100644 (file)
@@ -1,6 +1,5 @@
 #include "cache.h"
 #include "wt-status.h"
-#include "color.h"
 #include "object.h"
 #include "dir.h"
 #include "commit.h"
 #include "run-command.h"
 #include "remote.h"
 
-int wt_status_relative_paths = 1;
-int wt_status_use_color = -1;
-int wt_status_submodule_summary;
-static char wt_status_colors[][COLOR_MAXLEN] = {
+static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
        GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
        GIT_COLOR_RED,    /* WT_STATUS_CHANGED */
        GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
        GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
+       GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
 };
 
-enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-
-static int parse_status_slot(const char *var, int offset)
-{
-       if (!strcasecmp(var+offset, "header"))
-               return WT_STATUS_HEADER;
-       if (!strcasecmp(var+offset, "updated")
-               || !strcasecmp(var+offset, "added"))
-               return WT_STATUS_UPDATED;
-       if (!strcasecmp(var+offset, "changed"))
-               return WT_STATUS_CHANGED;
-       if (!strcasecmp(var+offset, "untracked"))
-               return WT_STATUS_UNTRACKED;
-       if (!strcasecmp(var+offset, "nobranch"))
-               return WT_STATUS_NOBRANCH;
-       die("bad config variable '%s'", var);
-}
-
-static const char *color(int slot)
+static const char *color(int slot, struct wt_status *s)
 {
-       return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
+       return s->use_color > 0 ? s->color_palette[slot] : "";
 }
 
 void wt_status_prepare(struct wt_status *s)
@@ -51,17 +30,40 @@ void wt_status_prepare(struct wt_status *s)
        const char *head;
 
        memset(s, 0, sizeof(*s));
+       memcpy(s->color_palette, default_wt_status_colors,
+              sizeof(default_wt_status_colors));
+       s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+       s->use_color = -1;
+       s->relative_paths = 1;
        head = resolve_ref("HEAD", sha1, 0, NULL);
        s->branch = head ? xstrdup(head) : NULL;
        s->reference = "HEAD";
        s->fp = stdout;
        s->index_file = get_index_file();
+       s->change.strdup_strings = 1;
+       s->untracked.strdup_strings = 1;
+}
+
+static void wt_status_print_unmerged_header(struct wt_status *s)
+{
+       const char *c = color(WT_STATUS_HEADER, s);
+       color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+       if (!advice_status_hints)
+               return;
+       if (!s->is_initial)
+               color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+       else
+               color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
+       color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to mark resolution)");
+       color_fprintf_ln(s->fp, c, "#");
 }
 
 static void wt_status_print_cached_header(struct wt_status *s)
 {
-       const char *c = color(WT_STATUS_HEADER);
+       const char *c = color(WT_STATUS_HEADER, s);
        color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+       if (!advice_status_hints)
+               return;
        if (!s->is_initial) {
                color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
        } else {
@@ -73,8 +75,10 @@ static void wt_status_print_cached_header(struct wt_status *s)
 static void wt_status_print_dirty_header(struct wt_status *s,
                                         int has_deleted)
 {
-       const char *c = color(WT_STATUS_HEADER);
+       const char *c = color(WT_STATUS_HEADER, s);
        color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+       if (!advice_status_hints)
+               return;
        if (!has_deleted)
                color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
        else
@@ -85,31 +89,73 @@ static void wt_status_print_dirty_header(struct wt_status *s,
 
 static void wt_status_print_untracked_header(struct wt_status *s)
 {
-       const char *c = color(WT_STATUS_HEADER);
+       const char *c = color(WT_STATUS_HEADER, s);
        color_fprintf_ln(s->fp, c, "# Untracked files:");
+       if (!advice_status_hints)
+               return;
        color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to include in what will be committed)");
        color_fprintf_ln(s->fp, c, "#");
 }
 
 static void wt_status_print_trailer(struct wt_status *s)
 {
-       color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+       color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
 }
 
 #define quote_path quote_path_relative
 
-static void wt_status_print_filepair(struct wt_status *s,
-                                    int t, struct diff_filepair *p)
+static void wt_status_print_unmerged_data(struct wt_status *s,
+                                         struct string_list_item *it)
+{
+       const char *c = color(WT_STATUS_UNMERGED, s);
+       struct wt_status_change_data *d = it->util;
+       struct strbuf onebuf = STRBUF_INIT;
+       const char *one, *how = "bug";
+
+       one = quote_path(it->string, -1, &onebuf, s->prefix);
+       color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+       switch (d->stagemask) {
+       case 1: how = "both deleted:"; break;
+       case 2: how = "added by us:"; break;
+       case 3: how = "deleted by them:"; break;
+       case 4: how = "added by them:"; break;
+       case 5: how = "deleted by us:"; break;
+       case 6: how = "both added:"; break;
+       case 7: how = "both modified:"; break;
+       }
+       color_fprintf(s->fp, c, "%-20s%s\n", how, one);
+       strbuf_release(&onebuf);
+}
+
+static void wt_status_print_change_data(struct wt_status *s,
+                                       int change_type,
+                                       struct string_list_item *it)
 {
-       const char *c = color(t);
+       struct wt_status_change_data *d = it->util;
+       const char *c = color(change_type, s);
+       int status = status;
+       char *one_name;
+       char *two_name;
        const char *one, *two;
        struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
 
-       one = quote_path(p->one->path, -1, &onebuf, s->prefix);
-       two = quote_path(p->two->path, -1, &twobuf, s->prefix);
+       one_name = two_name = it->string;
+       switch (change_type) {
+       case WT_STATUS_UPDATED:
+               status = d->index_status;
+               if (d->head_path)
+                       one_name = d->head_path;
+               break;
+       case WT_STATUS_CHANGED:
+               status = d->worktree_status;
+               break;
+       }
+
+       one = quote_path(one_name, -1, &onebuf, s->prefix);
+       two = quote_path(two_name, -1, &twobuf, s->prefix);
 
-       color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
-       switch (p->status) {
+       color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+       switch (status) {
        case DIFF_STATUS_ADDED:
                color_fprintf(s->fp, c, "new file:   %s", one);
                break;
@@ -135,64 +181,114 @@ static void wt_status_print_filepair(struct wt_status *s,
                color_fprintf(s->fp, c, "unmerged:   %s", one);
                break;
        default:
-               die("bug: unhandled diff status %c", p->status);
+               die("bug: unhandled diff status %c", status);
        }
        fprintf(s->fp, "\n");
        strbuf_release(&onebuf);
        strbuf_release(&twobuf);
 }
 
-static void wt_status_print_updated_cb(struct diff_queue_struct *q,
-               struct diff_options *options,
-               void *data)
+static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
+                                        struct diff_options *options,
+                                        void *data)
 {
        struct wt_status *s = data;
-       int shown_header = 0;
        int i;
+
+       if (!q->nr)
+               return;
+       s->workdir_dirty = 1;
        for (i = 0; i < q->nr; i++) {
-               if (q->queue[i]->status == 'U')
-                       continue;
-               if (!shown_header) {
-                       wt_status_print_cached_header(s);
-                       s->commitable = 1;
-                       shown_header = 1;
+               struct diff_filepair *p;
+               struct string_list_item *it;
+               struct wt_status_change_data *d;
+
+               p = q->queue[i];
+               it = string_list_insert(p->one->path, &s->change);
+               d = it->util;
+               if (!d) {
+                       d = xcalloc(1, sizeof(*d));
+                       it->util = d;
                }
-               wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);
+               if (!d->worktree_status)
+                       d->worktree_status = p->status;
        }
-       if (shown_header)
-               wt_status_print_trailer(s);
 }
 
-static void wt_status_print_changed_cb(struct diff_queue_struct *q,
-                        struct diff_options *options,
-                        void *data)
+static int unmerged_mask(const char *path)
+{
+       int pos, mask;
+       struct cache_entry *ce;
+
+       pos = cache_name_pos(path, strlen(path));
+       if (0 <= pos)
+               return 0;
+
+       mask = 0;
+       pos = -pos-1;
+       while (pos < active_nr) {
+               ce = active_cache[pos++];
+               if (strcmp(ce->name, path) || !ce_stage(ce))
+                       break;
+               mask |= (1 << (ce_stage(ce) - 1));
+       }
+       return mask;
+}
+
+static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
+                                        struct diff_options *options,
+                                        void *data)
 {
        struct wt_status *s = data;
        int i;
-       if (q->nr) {
-               int has_deleted = 0;
-               s->workdir_dirty = 1;
-               for (i = 0; i < q->nr; i++)
-                       if (q->queue[i]->status == DIFF_STATUS_DELETED) {
-                               has_deleted = 1;
-                               break;
-                       }
-               wt_status_print_dirty_header(s, has_deleted);
+
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p;
+               struct string_list_item *it;
+               struct wt_status_change_data *d;
+
+               p = q->queue[i];
+               it = string_list_insert(p->two->path, &s->change);
+               d = it->util;
+               if (!d) {
+                       d = xcalloc(1, sizeof(*d));
+                       it->util = d;
+               }
+               if (!d->index_status)
+                       d->index_status = p->status;
+               switch (p->status) {
+               case DIFF_STATUS_COPIED:
+               case DIFF_STATUS_RENAMED:
+                       d->head_path = xstrdup(p->one->path);
+                       break;
+               case DIFF_STATUS_UNMERGED:
+                       d->stagemask = unmerged_mask(p->two->path);
+                       break;
+               }
        }
-       for (i = 0; i < q->nr; i++)
-               wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
-       if (q->nr)
-               wt_status_print_trailer(s);
 }
 
-static void wt_status_print_updated(struct wt_status *s)
+static void wt_status_collect_changes_worktree(struct wt_status *s)
 {
        struct rev_info rev;
+
+       init_revisions(&rev, NULL);
+       setup_revisions(0, NULL, &rev, NULL);
+       rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+       rev.diffopt.format_callback = wt_status_collect_changed_cb;
+       rev.diffopt.format_callback_data = s;
+       run_diff_files(&rev, 0);
+}
+
+static void wt_status_collect_changes_index(struct wt_status *s)
+{
+       struct rev_info rev;
+
        init_revisions(&rev, NULL);
        setup_revisions(0, NULL, &rev,
                s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
-       rev.diffopt.format_callback = wt_status_print_updated_cb;
+       rev.diffopt.format_callback = wt_status_collect_updated_cb;
        rev.diffopt.format_callback_data = s;
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 200;
@@ -200,15 +296,155 @@ static void wt_status_print_updated(struct wt_status *s)
        run_diff_index(&rev, 1);
 }
 
+static void wt_status_collect_changes_initial(struct wt_status *s)
+{
+       int i;
+
+       for (i = 0; i < active_nr; i++) {
+               struct string_list_item *it;
+               struct wt_status_change_data *d;
+               struct cache_entry *ce = active_cache[i];
+
+               it = string_list_insert(ce->name, &s->change);
+               d = it->util;
+               if (!d) {
+                       d = xcalloc(1, sizeof(*d));
+                       it->util = d;
+               }
+               if (ce_stage(ce)) {
+                       d->index_status = DIFF_STATUS_UNMERGED;
+                       d->stagemask |= (1 << (ce_stage(ce) - 1));
+               }
+               else
+                       d->index_status = DIFF_STATUS_ADDED;
+       }
+}
+
+static void wt_status_collect_untracked(struct wt_status *s)
+{
+       int i;
+       struct dir_struct dir;
+
+       if (!s->show_untracked_files)
+               return;
+       memset(&dir, 0, sizeof(dir));
+       if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
+               dir.flags |=
+                       DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
+       setup_standard_excludes(&dir);
+
+       fill_directory(&dir, NULL);
+       for (i = 0; i < dir.nr; i++) {
+               struct dir_entry *ent = dir.entries[i];
+               if (!cache_name_is_other(ent->name, ent->len))
+                       continue;
+               s->workdir_untracked = 1;
+               string_list_insert(ent->name, &s->untracked);
+       }
+}
+
+void wt_status_collect(struct wt_status *s)
+{
+       wt_status_collect_changes_worktree(s);
+
+       if (s->is_initial)
+               wt_status_collect_changes_initial(s);
+       else
+               wt_status_collect_changes_index(s);
+       wt_status_collect_untracked(s);
+}
+
+static void wt_status_print_unmerged(struct wt_status *s)
+{
+       int shown_header = 0;
+       int i;
+
+       for (i = 0; i < s->change.nr; i++) {
+               struct wt_status_change_data *d;
+               struct string_list_item *it;
+               it = &(s->change.items[i]);
+               d = it->util;
+               if (!d->stagemask)
+                       continue;
+               if (!shown_header) {
+                       wt_status_print_unmerged_header(s);
+                       shown_header = 1;
+               }
+               wt_status_print_unmerged_data(s, it);
+       }
+       if (shown_header)
+               wt_status_print_trailer(s);
+
+}
+
+static void wt_status_print_updated(struct wt_status *s)
+{
+       int shown_header = 0;
+       int i;
+
+       for (i = 0; i < s->change.nr; i++) {
+               struct wt_status_change_data *d;
+               struct string_list_item *it;
+               it = &(s->change.items[i]);
+               d = it->util;
+               if (!d->index_status ||
+                   d->index_status == DIFF_STATUS_UNMERGED)
+                       continue;
+               if (!shown_header) {
+                       wt_status_print_cached_header(s);
+                       s->commitable = 1;
+                       shown_header = 1;
+               }
+               wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
+       }
+       if (shown_header)
+               wt_status_print_trailer(s);
+}
+
+/*
+ * -1 : has delete
+ *  0 : no change
+ *  1 : some change but no delete
+ */
+static int wt_status_check_worktree_changes(struct wt_status *s)
+{
+       int i;
+       int changes = 0;
+
+       for (i = 0; i < s->change.nr; i++) {
+               struct wt_status_change_data *d;
+               d = s->change.items[i].util;
+               if (!d->worktree_status ||
+                   d->worktree_status == DIFF_STATUS_UNMERGED)
+                       continue;
+               changes = 1;
+               if (d->worktree_status == DIFF_STATUS_DELETED)
+                       return -1;
+       }
+       return changes;
+}
+
 static void wt_status_print_changed(struct wt_status *s)
 {
-       struct rev_info rev;
-       init_revisions(&rev, "");
-       setup_revisions(0, NULL, &rev, NULL);
-       rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
-       rev.diffopt.format_callback = wt_status_print_changed_cb;
-       rev.diffopt.format_callback_data = s;
-       run_diff_files(&rev, 0);
+       int i;
+       int worktree_changes = wt_status_check_worktree_changes(s);
+
+       if (!worktree_changes)
+               return;
+
+       wt_status_print_dirty_header(s, worktree_changes < 0);
+
+       for (i = 0; i < s->change.nr; i++) {
+               struct wt_status_change_data *d;
+               struct string_list_item *it;
+               it = &(s->change.items[i]);
+               d = it->util;
+               if (!d->worktree_status ||
+                   d->worktree_status == DIFF_STATUS_UNMERGED)
+                       continue;
+               wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
+       }
+       wt_status_print_trailer(s);
 }
 
 static void wt_status_print_submodule_summary(struct wt_status *s)
@@ -228,7 +464,7 @@ static void wt_status_print_submodule_summary(struct wt_status *s)
                NULL
        };
 
-       sprintf(summary_limit, "%d", wt_status_submodule_summary);
+       sprintf(summary_limit, "%d", s->submodule_summary);
        snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
 
        memset(&sm_summary, 0, sizeof(sm_summary));
@@ -243,32 +479,20 @@ static void wt_status_print_submodule_summary(struct wt_status *s)
 
 static void wt_status_print_untracked(struct wt_status *s)
 {
-       struct dir_struct dir;
        int i;
-       int shown_header = 0;
        struct strbuf buf = STRBUF_INIT;
 
-       memset(&dir, 0, sizeof(dir));
-
-       if (!s->untracked)
-               dir.flags |=
-                       DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
-       setup_standard_excludes(&dir);
+       if (!s->untracked.nr)
+               return;
 
-       read_directory(&dir, ".", "", 0, NULL);
-       for(i = 0; i < dir.nr; i++) {
-               struct dir_entry *ent = dir.entries[i];
-               if (!cache_name_is_other(ent->name, ent->len))
-                       continue;
-               if (!shown_header) {
-                       s->workdir_untracked = 1;
-                       wt_status_print_untracked_header(s);
-                       shown_header = 1;
-               }
-               color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
-               color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s",
-                               quote_path(ent->name, ent->len,
-                                       &buf, s->prefix));
+       wt_status_print_untracked_header(s);
+       for (i = 0; i < s->untracked.nr; i++) {
+               struct string_list_item *it;
+               it = &(s->untracked.items[i]);
+               color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+               color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s",
+                                quote_path(it->string, strlen(it->string),
+                                           &buf, s->prefix));
        }
        strbuf_release(&buf);
 }
@@ -310,15 +534,15 @@ static void wt_status_print_tracking(struct wt_status *s)
                return;
 
        for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
-               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
+               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
                                 "# %.*s", (int)(ep - cp), cp);
-       color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+       color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
 }
 
 void wt_status_print(struct wt_status *s)
 {
        unsigned char sha1[20];
-       const char *branch_color = color(WT_STATUS_HEADER);
+       const char *branch_color = color(WT_STATUS_HEADER, s);
 
        s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
        if (s->branch) {
@@ -328,26 +552,29 @@ void wt_status_print(struct wt_status *s)
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_name = "";
-                       branch_color = color(WT_STATUS_NOBRANCH);
+                       branch_color = color(WT_STATUS_NOBRANCH, s);
                        on_what = "Not currently on any branch.";
                }
-               color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
+               color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
                color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
                if (!s->is_initial)
                        wt_status_print_tracking(s);
        }
 
+       wt_status_collect(s);
+
        if (s->is_initial) {
-               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
-               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
-               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
+               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
        }
 
        wt_status_print_updated(s);
+       wt_status_print_unmerged(s);
        wt_status_print_changed(s);
-       if (wt_status_submodule_summary)
+       if (s->submodule_summary)
                wt_status_print_submodule_summary(s);
-       if (show_untracked_files)
+       if (s->show_untracked_files)
                wt_status_print_untracked(s);
        else if (s->commitable)
                 fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
@@ -361,53 +588,13 @@ void wt_status_print(struct wt_status *s)
                        ; /* nothing */
                else if (s->workdir_dirty)
                        printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
-               else if (s->workdir_untracked)
+               else if (s->untracked.nr)
                        printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
                else if (s->is_initial)
                        printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
-               else if (!show_untracked_files)
+               else if (!s->show_untracked_files)
                        printf("nothing to commit (use -u to show untracked files)\n");
                else
                        printf("nothing to commit (working directory clean)\n");
        }
 }
-
-int git_status_config(const char *k, const char *v, void *cb)
-{
-       if (!strcmp(k, "status.submodulesummary")) {
-               int is_bool;
-               wt_status_submodule_summary = git_config_bool_or_int(k, v, &is_bool);
-               if (is_bool && wt_status_submodule_summary)
-                       wt_status_submodule_summary = -1;
-               return 0;
-       }
-       if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
-               wt_status_use_color = git_config_colorbool(k, v, -1);
-               return 0;
-       }
-       if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
-               int slot = parse_status_slot(k, 13);
-               if (!v)
-                       return config_error_nonbool(k);
-               color_parse(v, k, wt_status_colors[slot]);
-               return 0;
-       }
-       if (!strcmp(k, "status.relativepaths")) {
-               wt_status_relative_paths = git_config_bool(k, v);
-               return 0;
-       }
-       if (!strcmp(k, "status.showuntrackedfiles")) {
-               if (!v)
-                       return config_error_nonbool(k);
-               else if (!strcmp(v, "no"))
-                       show_untracked_files = SHOW_NO_UNTRACKED_FILES;
-               else if (!strcmp(v, "normal"))
-                       show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-               else if (!strcmp(v, "all"))
-                       show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
-               else
-                       return error("Invalid untracked files mode '%s'", v);
-               return 0;
-       }
-       return git_diff_ui_config(k, v, cb);
-}
index 78add09bd67c727babb61cd1eaa773bcd0c6e55e..a0e75177bec925e95b4257811aae0bdee93c41f1 100644 (file)
@@ -2,13 +2,16 @@
 #define STATUS_H
 
 #include <stdio.h>
+#include "string-list.h"
+#include "color.h"
 
 enum color_wt_status {
-       WT_STATUS_HEADER,
+       WT_STATUS_HEADER = 0,
        WT_STATUS_UPDATED,
        WT_STATUS_CHANGED,
        WT_STATUS_UNTRACKED,
        WT_STATUS_NOBRANCH,
+       WT_STATUS_UNMERGED,
 };
 
 enum untracked_status_type {
@@ -16,7 +19,13 @@ enum untracked_status_type {
        SHOW_NORMAL_UNTRACKED_FILES,
        SHOW_ALL_UNTRACKED_FILES
 };
-extern enum untracked_status_type show_untracked_files;
+
+struct wt_status_change_data {
+       int worktree_status;
+       int index_status;
+       int stagemask;
+       char *head_path;
+};
 
 struct wt_status {
        int is_initial;
@@ -24,8 +33,13 @@ struct wt_status {
        const char *reference;
        int verbose;
        int amend;
-       int untracked;
        int nowarn;
+       int use_color;
+       int relative_paths;
+       int submodule_summary;
+       enum untracked_status_type show_untracked_files;
+       char color_palette[WT_STATUS_UNMERGED+1][COLOR_MAXLEN];
+
        /* These are computed during processing of the individual sections */
        int commitable;
        int workdir_dirty;
@@ -33,12 +47,12 @@ struct wt_status {
        const char *index_file;
        FILE *fp;
        const char *prefix;
+       struct string_list change;
+       struct string_list untracked;
 };
 
-int git_status_config(const char *var, const char *value, void *cb);
-extern int wt_status_use_color;
-extern int wt_status_relative_paths;
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
+void wt_status_collect(struct wt_status *s);
 
 #endif /* STATUS_H */
index b9b0db8d86615d6ca1046b932772f3d9750a8062..01f14fb50f7cf1387898a0c8db44f966ce07b720 100644 (file)
@@ -309,6 +309,21 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags)
        }
 }
 
+void xdiff_clear_find_func(xdemitconf_t *xecfg)
+{
+       if (xecfg->find_func) {
+               int i;
+               struct ff_regs *regs = xecfg->find_func_priv;
+
+               for (i = 0; i < regs->nr; i++)
+                       regfree(&regs->array[i].re);
+               free(regs->array);
+               free(regs);
+               xecfg->find_func = NULL;
+               xecfg->find_func_priv = NULL;
+       }
+}
+
 int git_xmerge_style = -1;
 
 int git_xmerge_config(const char *var, const char *value, void *cb)
index 7352b9a9c204c2b1d4ca9df5ce040fe22d6f521c..55572c39a10dee336355f816b324946fc087d6e7 100644 (file)
@@ -21,6 +21,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename);
 int buffer_is_binary(const char *ptr, unsigned long size);
 
 extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
+extern void xdiff_clear_find_func(xdemitconf_t *xecfg);
 extern int git_xmerge_config(const char *var, const char *value, void *cb);
 extern int git_xmerge_style;
 
index 1ebab687f77218d1520a4527214170eeab176d31..da67c04357dfe4d3283c589f5d47be9c5f2b7fcf 100644 (file)
@@ -26,7 +26,7 @@
 
 #define XDL_MAX_COST_MIN 256
 #define XDL_HEUR_MIN_COST 256
-#define XDL_LINE_MAX (long)((1UL << (8 * sizeof(long) - 1)) - 1)
+#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
 #define XDL_SNAKE_CNT 20
 #define XDL_K_HEUR 4
 
index d9737f04c220645aa762d79ff14a84855721ffda..1cb65a95166a8cb60af590118f59d78aa4d24b74 100644 (file)
@@ -563,23 +563,22 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
                return -1;
        }
        status = 0;
-       if (xscr1 || xscr2) {
-               if (!xscr1) {
-                       result->ptr = xdl_malloc(mf2->size);
-                       memcpy(result->ptr, mf2->ptr, mf2->size);
-                       result->size = mf2->size;
-               } else if (!xscr2) {
-                       result->ptr = xdl_malloc(mf1->size);
-                       memcpy(result->ptr, mf1->ptr, mf1->size);
-                       result->size = mf1->size;
-               } else {
-                       status = xdl_do_merge(&xe1, xscr1, name1,
-                                             &xe2, xscr2, name2,
-                                             flags, xpp, result);
-               }
-               xdl_free_script(xscr1);
-               xdl_free_script(xscr2);
+       if (!xscr1) {
+               result->ptr = xdl_malloc(mf2->size);
+               memcpy(result->ptr, mf2->ptr, mf2->size);
+               result->size = mf2->size;
+       } else if (!xscr2) {
+               result->ptr = xdl_malloc(mf1->size);
+               memcpy(result->ptr, mf1->ptr, mf1->size);
+               result->size = mf1->size;
+       } else {
+               status = xdl_do_merge(&xe1, xscr1, name1,
+                                     &xe2, xscr2, name2,
+                                     flags, xpp, result);
        }
+       xdl_free_script(xscr1);
+       xdl_free_script(xscr2);
+
        xdl_free_env(&xe1);
        xdl_free_env(&xe2);
 
index 04ad468702209b77427e635370d41001986042ce..bc12f298953a4e72b323f73607278028ec4a2805 100644 (file)
@@ -190,48 +190,66 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
        int i1, i2;
 
+       if (!(flags & XDF_WHITESPACE_FLAGS))
+               return s1 == s2 && !memcmp(l1, l2, s1);
+
+       i1 = 0;
+       i2 = 0;
+
+       /*
+        * -w matches everything that matches with -b, and -b in turn
+        * matches everything that matches with --ignore-space-at-eol.
+        *
+        * Each flavor of ignoring needs different logic to skip whitespaces
+        * while we have both sides to compare.
+        */
        if (flags & XDF_IGNORE_WHITESPACE) {
-               for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
-                       if (isspace(l1[i1]))
-                               while (isspace(l1[i1]) && i1 < s1)
-                                       i1++;
-                       if (isspace(l2[i2]))
-                               while (isspace(l2[i2]) && i2 < s2)
-                                       i2++;
-                       if (i1 < s1 && i2 < s2 && l1[i1++] != l2[i2++])
+               goto skip_ws;
+               while (i1 < s1 && i2 < s2) {
+                       if (l1[i1++] != l2[i2++])
                                return 0;
+               skip_ws:
+                       while (i1 < s1 && isspace(l1[i1]))
+                               i1++;
+                       while (i2 < s2 && isspace(l2[i2]))
+                               i2++;
                }
-               return (i1 >= s1 && i2 >= s2);
        } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
-               for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
-                       if (isspace(l1[i1])) {
-                               if (!isspace(l2[i2]))
-                                       return 0;
-                               while (isspace(l1[i1]) && i1 < s1)
-                                       i1++;
-                               while (isspace(l2[i2]) && i2 < s2)
-                                       i2++;
-                       } else if (l1[i1++] != l2[i2++])
-                               return 0;
-               }
-               return (i1 >= s1 && i2 >= s2);
-       } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
-               for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
-                       if (l1[i1] != l2[i2]) {
+               while (i1 < s1 && i2 < s2) {
+                       if (isspace(l1[i1]) && isspace(l2[i2])) {
+                               /* Skip matching spaces and try again */
                                while (i1 < s1 && isspace(l1[i1]))
                                        i1++;
                                while (i2 < s2 && isspace(l2[i2]))
                                        i2++;
-                               if (i1 < s1 || i2 < s2)
-                                       return 0;
-                               return 1;
+                               continue;
                        }
+                       if (l1[i1++] != l2[i2++])
+                               return 0;
+               }
+       } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
+               while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
+                       ; /* keep going */
+       }
+
+       /*
+        * After running out of one side, the remaining side must have
+        * nothing but whitespace for the lines to match.  Note that
+        * ignore-whitespace-at-eol case may break out of the loop
+        * while there still are characters remaining on both lines.
+        */
+       if (i1 < s1) {
+               while (i1 < s1 && isspace(l1[i1]))
                        i1++;
+               if (s1 != i1)
+                       return 0;
+       }
+       if (i2 < s2) {
+               while (i2 < s2 && isspace(l2[i2]))
                        i2++;
-               }
-               return i1 >= s1 && i2 >= s2;
-       } else
-               return s1 == s2 && !memcmp(l1, l2, s1);
+               return (s2 == i2);
+       }
+       return 1;
 }
 
 static unsigned long xdl_hash_record_with_whitespace(char const **data,
@@ -242,18 +260,20 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
        for (; ptr < top && *ptr != '\n'; ptr++) {
                if (isspace(*ptr)) {
                        const char *ptr2 = ptr;
+                       int at_eol;
                        while (ptr + 1 < top && isspace(ptr[1])
                                        && ptr[1] != '\n')
                                ptr++;
+                       at_eol = (top <= ptr + 1 || ptr[1] == '\n');
                        if (flags & XDF_IGNORE_WHITESPACE)
                                ; /* already handled */
                        else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
-                                       && ptr[1] != '\n') {
+                                && !at_eol) {
                                ha += (ha << 5);
                                ha ^= (unsigned long) ' ';
                        }
                        else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
-                                       && ptr[1] != '\n') {
+                                && !at_eol) {
                                while (ptr2 != ptr + 1) {
                                        ha += (ha << 5);
                                        ha ^= (unsigned long) *ptr2;